Pagina 1 di 3 1 2 3 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 23
  1. #1
    Utente di HTML.it
    Registrato dal
    Jun 2008
    Messaggi
    1,316

    [HackMe] Nuovo metodo più sicuro per storage di password

    Ciao ragazzi, perdonate il titolo fantasioso.

    Ho inventato un nuovo tipo di implementazione logica per lo storage delle password, invece della solita combinazione user/password.
    Volevo dei consigli da voi, sia perchè lo possiate confutare e solo nel caso fosse effettivamente più sicuro, vorrei implementarlo nel kernel UNIX al posto di quello attuale (solo che non posso leggermi tutto il kernel, perciò se avete articoli utili per ciò che riguarda l'astrazione del login linkatemi pure per favore).

    Cominciamo con una premessa: è ovvio che il server deve essere sicuro, l'implementazione è solo una soluzione aggiuntiva nel quale l'attaccante è riuscito a bypassare le misure precedenti: come lo può essere criptare una password, o disabilitare eventuali direttive, .

    Ho creato un POC in MySQL/MariaDB, perchè secondo me rende bene il concetto ma si può ovviamente implementare anche in NoSQL, o addirittura modificando la struttura della gestione delle password in /etc/shadow utilizzando il nono campo come indice del file che contiene delle password randomiche, fra cui quella dell'utente (.

    File .SQL (Ho semplificato il più possibile il codice per rendere il tutto il più comprensibile possibile: se non volete incorrere in concorrenze se effettuate un inserimento massivo di utenti deve essere applicato il LOCK)

    Nella teoria se l'attaccante accede ai dati privati a volte si ritrova a dover decriptare la password dell'utente interessato (e ogni tanto lo fa come ultima spiaggia nel caso il server sia ben configurato e protetto), mentre così si ritrova con n passwords non sapendo quale è quella che deve decriptare.

    Per esempio in sistemi amministrativi o login root potrebbero auto-generare nMila password hash "fake".

    Grazie a chiunque dimostrerà interessamento.

    Se non volete cliccare sul link:
    codice:
    CREATE DATABASE test CHARSET utf8 COLLATE utf8_bin;
     
    use test;
     
    SET GLOBAL log_bin_trust_function_creators = 1;
     
    -----------------------------------------------------------------------------
    -- Configurazione:
    --    @RND_STR_LENGHT, lunghezza della stringa utilizzata per il salt della password;
    --    @RND_STR_CHARS, caratteri utilizzati per la generazione della stringa casuale;
    ----------------------------------------------------------------------------
    SET @RND_STR_LENGHT = 255;
    SET @RND_STR_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$/()=?^[]+*@-_<>.';
     
    CREATE TABLE users(
        id BIGINT UNSIGNED AUTO_INCREMENT,
        email VARCHAR(255) NOT NULL,
        salt CHAR(255) NOT NULL,
        PRIMARY KEY(id)
    )ENGINE=innoDB;
     
    CREATE TABLE hashes(
        hash CHAR(128) NOT NULL,
        UNIQUE KEY(hash)
    )ENGINE=innoDB;
     
    ----------------------------------------------------------------------------
    -- Genera la stringa casuale da 255 caratteri
    ----------------------------------------------------------------------------
    DELIMITER $$
     
    CREATE FUNCTION GEN_RND_STR() RETURNS VARCHAR(255) NOT DETERMINISTIC
    BEGIN
        SET @rndStr = '';
        WHILE LENGTH(@rndStr) < @RND_STR_LENGHT DO
            SET @rndStr = CONCAT(@rndStr, SUBSTRING(@RND_STR_CHARS FROM FLOOR(RAND() * LENGTH(@RND_STR_CHARS) + 1) FOR 1));
        END WHILE;
        RETURN @rndStr;
    END$$
     
    DELIMITER ;
     
    ----------------------------------------------------------------------------
    -- Registra l'utente
    ----------------------------------------------------------------------------
    DELIMITER $$
     
    CREATE PROCEDURE REG_USER(
        IN _email VARCHAR(255),
        IN _password VARCHAR(255)
    ) BEGIN
        # Ipotizzando che prima delle registrazione venga verificato che l email
        # inserita non sia già presente.
     
        # Inserisce una hash univoca, utilizziamo il loop per rigenerarlo nel caso
        # sia già presente nella tabella.
        #
        # L hash è generato dalla password dell utente concatenato al salt:
        # SHA2(CONCAT(salt, password), 512)
        hashGen: LOOP
            SET @rndSalt = GEN_RND_STR();
            SET @hash = SHA2(CONCAT(@rndSalt, _password), 512);
            SET @SQL = '
                SELECT @t := COUNT(*)
                  FROM hashes
                 WHERE ? = hash';
            PREPARE stmt FROM @SQL;
            EXECUTE stmt USING @hash;
            DEALLOCATE PREPARE stmt;
     
            IF ((SELECT @t) = 0) THEN
                LEAVE hashGen;
            END IF;
        END LOOP hashGen;
     
        INSERT INTO users (email, salt)
            VALUES (_email, @rndSalt);
        INSERT INTO hashes
             VALUES (@hash);
     
        # Genera una tabella casuale nella quale vengono riordinate le hash.
        # Questo è necessario perchè se fossero inserite nello stesso ordine nel
        # quale sono inseriti gli utenti basterebbe un "LIMIT USER_ID" ed avere
        # come ultimo elemento l'hash dell'utente.
        CREATE TEMPORARY TABLE tmpHashes(
            hash CHAR(128) NOT NULL,
            UNIQUE KEY(hash)
        )ENGINE=innoDB
        AS (
            SELECT *
              FROM hashes
             ORDER BY RAND()
        );
     
        # Cancella i dati e ripopola la tabella delle hash.
        TRUNCATE TABLE hashes;
        INSERT INTO hashes SELECT * FROM hashes;
     
    END$$
     
    DELIMITER ;
     
    ----------------------------------------------------------------------------
    -- Verifica le credenziali
    ----------------------------------------------------------------------------
    DELIMITER $$
     
    CREATE PROCEDURE LOGIN(
        IN _email VARCHAR(255),
        IN _password VARCHAR(255),
        OUT _userId BIGINT UNSIGNED
    ) BEGIN
        SELECT u.id
          INTO _userId
          FROM users u
               INNER JOIN hashes h
                       ON h.hash = SHA2(CONCAT(u.salt, _password), 512)
         WHERE u.email = _email;
    END$$
     
    DELIMITER ;
     
    CALL REG_USER('email@test.com', 's3cr3tp4ss');
    CALL REG_USER('kitty@yep.org', 'hello_kitty');
    CALL REG_USER('jhon@fbi.gov', '_2jsd82+*é称ç称XVBZ__-');
    SELECT * FROM users;
     
    CALL LOGIN('email@test.com', 's3cr3tp4ss', @userId);
    SELECT @userId;
     
    CALL LOGIN('email@test.com', 'invalid_password', @userId);
    SELECT @userId;
     
    
    DROP DATABASE test;

  2. #2
    Mm al di là del codice specifico, se ho ben capito la tua idea sarebbe non associare esplicitamente l'hash di salt+password all'utente, ma tenerli "alla rinfusa" in un'unica tabella, e usare il salt per evitare collisioni (oltre agli usuali problemi di sicurezza che risolve il salt).

    Il miglioramento di sicurezza dovrebbe derivare dal fatto che, mancando l'associazione utente-password, dovrebbe essere più complesso cercare di craccare una password di un utente specifico, dato che non sai qual è l'hash corrispondente, ed è inoltre possibile aggiungere degli hash aggiuntivi "per depistaggio" alla tabella.

    Mi confermi che ho compreso la cosa correttamente?
    Amaro C++, il gusto pieno dell'undefined behavior.

  3. #3
    Utente di HTML.it L'avatar di Scara95
    Registrato dal
    Jul 2009
    residenza
    Zimella (VR)
    Messaggi
    2,590
    Qualsiasi password restituisca uno degli hash presenti nella tua tabella hashes viene accettata, quindi invece di rendere il tutto più sicuro l'hai reso meno sicuro perché ci sono più possibilità fra cui scegliere
    "Quid enim est, quod contra vim sine vi fieri possit?" - Cicerone, Ad Familiares

  4. #4
    Utente di HTML.it
    Registrato dal
    Jun 2008
    Messaggi
    1,316
    Quote Originariamente inviata da MItaly Visualizza il messaggio
    Mm al di là del codice specifico, se ho ben capito la tua idea sarebbe non associare esplicitamente l'hash di salt+password all'utente, ma tenerli "alla rinfusa" in un'unica tabella, e usare il salt per evitare collisioni (oltre agli usuali problemi di sicurezza che risolve il salt).

    Il miglioramento di sicurezza dovrebbe derivare dal fatto che, mancando l'associazione utente-password, dovrebbe essere più complesso cercare di craccare una password di un utente specifico, dato che non sai qual è l'hash corrispondente, ed è inoltre possibile aggiungere degli hash aggiuntivi "per depistaggio" alla tabella.

    Mi confermi che ho compreso la cosa correttamente?
    Si hai riassunto il punto

    Quote Originariamente inviata da Scara95 Visualizza il messaggio
    Qualsiasi password restituisca uno degli hash presenti nella tua tabella hashes viene accettata, quindi invece di rendere il tutto più sicuro l'hai reso meno sicuro perché ci sono più possibilità fra cui scegliere
    Non credo, hai letto il mio codice?

    Contando che viene generate anche un salt (che possiamo, perchè no, rendere univoco), in questo caso non esisterà mai una combinazione salt+password che dia il risultato di un'altra chiave UNIVOCA della hash.

    codice:
        UNIQUE KEY(hash)
    )ENGINE=innoDB;
    
    
    // ...
    
        # Inserisce una hash univoca, utilizziamo il loop per rigenerarlo nel caso
        # sia già presente nella tabella.
        #
        # L hash è generato dalla password dell utente concatenato al salt:
        # SHA2(CONCAT(salt, password), 512)
        hashGen: LOOP
            SET @rndSalt = GEN_RND_STR();
            SET @hash = SHA2(CONCAT(@rndSalt, _password), 512);
            SET @SQL = '
                SELECT @t := COUNT(*)
                  FROM hashes
                 WHERE ? = hash';
            PREPARE stmt FROM @SQL;
            EXECUTE stmt USING @hash;
            DEALLOCATE PREPARE stmt;
     
            IF ((SELECT @t) = 0) THEN
                LEAVE hashGen;
            END IF;
        END LOOP hashGen;

    Sei sicuro di ciò che stai dicendo? Puoi affermarlo con un esempio pratico?
    Sono curioso di una tua continuazione...
    Ultima modifica di zacca94; 14-10-2017 a 16:20

  5. #5
    Utente di HTML.it
    Registrato dal
    Jun 2008
    Messaggi
    1,316
    Mi rimangio ciò che ho detto: ora che ci penso meglio, potrebbe effettivamente essere, e come idea non mi fa impazzire.

    Ma quanto è probabile che una stringa di 255 chars di salt + [numero di stringhe che fanno da brute force per trovare una password qualsiasi], possa trovare una correlazione?

    Se rendo univoco anche il salt, teoricamente il problema dipende solo dalle collisioni, in questo caso le stesse (improbabili) collisioni che affliggono un qualsiasi database già ora...

  6. #6
    Utente di HTML.it
    Registrato dal
    Jun 2008
    Messaggi
    1,316
    Ho anche trovato questo
    Dove dice:

    A hash function accepts inputs of arbitrary (or at least very high) length, and produces a fixed-length output. There are more possible inputs than possible outputs, so collisions must exist. The whole point of a secure hash function is that it is "collision resistant", which means that while collisions must mathematically exist, it is very very hard to actually compute one. Thus, there is no known collision for SHA-256 and SHA-512, and the best known methods for computing one (by doing it on purpose) are so ludicrously expensive that they will not be applied soon (the whole US federal budget for a century would buy only a ridiculously small part of the task).
    Il rischio di questa collisione di cui parli, teoricamente, è attualmente, fortemente improbabile per non dire impossibile.


    Aggiungo una domanda per chi è bravo in matematica e ne capisce un pò di crittografia (non è il mio caso):
    Se io imponessi input di lunghezza fissa (password+salt = [sempre stesso numero di caratteri]) potrei prevenire la (im)probabilità matematica di collisione, se si, quale è questa lunghezza?
    Ultima modifica di zacca94; 14-10-2017 a 18:05

  7. #7
    Utente di HTML.it L'avatar di Scara95
    Registrato dal
    Jul 2009
    residenza
    Zimella (VR)
    Messaggi
    2,590
    Il punto chiave è che non mi serve conoscere la password originale: a me basta una stringa randomica che generi lo stesso hash. Ora supponi che la tua tabella abbia un milione di hash salvati. Adesso mi basta una stringa che genere un hash qualunque fra questi, il mio lavoro è appena diventato un milione di volte più facile! Luckyyy
    "Quid enim est, quod contra vim sine vi fieri possit?" - Cicerone, Ad Familiares

  8. #8
    Utente di HTML.it
    Registrato dal
    Jun 2008
    Messaggi
    1,316
    Si ho capito, e ti ho risposto con un link nel quale viene affermato che ciò è praticamente "impossibile".


    Però siccome vogliamo fare le cose per bene, ipotizziamo di voler scongiurare l'improbabile:
    Quindi mi riquoto:
    Aggiungo una domanda per chi è bravo in matematica e ne capisce un pò di crittografia (non è il mio caso):
    Se io imponessi input di lunghezza fissa (password+salt = [sempre stesso numero di caratteri]) potrei prevenire la (im)probabilità matematica di collisione, se si, quale è questa lunghezza?

  9. #9
    Utente di HTML.it L'avatar di minomic
    Registrato dal
    Nov 2010
    Messaggi
    635
    Ciao a tutti,
    Mi infilo anche io in questa discussione perché sono un ricercatore nel campo della crittografia e sono quindi interessato al tema. Premetto che la mia specialità non sono le hash functions, ma ho letto e conosco un po' di cose sul tema.

    Dopo aver letto i vari post, mi trovo d'accordo con Scara95: sembra che la possibilità di "sparare a caso" in una tabella più grande non possa che aumentare il rischio di collisioni. La questione della struttura salt+password non sembra rilevante: l'unica cosa che serve è una collisione nella funzione di hash. Ora, se hai un solo bersaglio allora hai una certa probabilità di successo. Ma in questo caso sembra che i bersagli possibili siano potenzialmente molti di più. Quindi è per forza più semplice trovare una collisione. Tra l'altro, anche la questione della lunghezza delle stringhe non sembra importante: qualunque input, di qualunque lunghezza, viene mappato su un output di lunghezza fissa. Quindi uno può semplicemente fare hash di input completamente casuali e di qualunque lunghezza. Ogni collisione porta ad un accesso non autorizzato.

    Ovviamente, tutto questo se ho capito bene il problema


  10. #10
    Va anche detta un'altra cosa.

    Da cos'è che dovrebbe proteggere questa cosa?

    Togliamo come vettore di attacco le rainbow tables o gli hash precalcolati da un dizionario - dato che in qualunque schema di memorizzazione di password decente c'è di mezzo del salt. Ammettendo che la funzione di hash sia robusta (come appunto SHA2), l'unica alternativa pratica rimane provare a calcolare gli hash (scelto un utente e quindi un salt), o partendo da un dizionario o proprio brute force su un certo alfabeto.

    Il tuo schema qui semplicemente rallenta il check che l'hash generato sia corretto, dato che bisogna cercarlo in una tabella invece di fare il confronto "a colpo secco"; questo però non è un ostacolo così tremendo - gli hash per loro stessa natura sono ben distribuiti, e quindi è facile costruire una struttura dati di ricerca estremamente veloce (per esempio, si può fare una prima tabella di lookup basata sui primi due byte degli hash che stia addirittura in cache L1, e andare a vedere i byte successivi "a colpo sicuro" solo se questi ci sono in tabella, o se gli hash non sono tanti fare addirittura una dicotomica magari "aiutata" dalla buona distribuzione degli hash).

    Rispetto al calcolo dell'hash in sé non mi aspetto che questa ricerca porti via un tempo così esagerato, e soprattutto esiste un sistema molto più semplice per ottenere un risultato di questo genere: hashare la password un numero esagerato di volte.

    Uno schema classico di questo tipo è il PBKDF2. In sintesi, la coppia salt-password viene calcolata e hashata non una sola volta, ma - ad esempio - 10000 (nelle iterazioni successive al posto della password viene usato l'hash generato al giro precedente). In questa maniera il calcolo di ogni hash viene rallentato di un numero di ordini di grandezza a piacere (dato che durante l'operazione normale non è un problema se il calcolo dell'hash della password impiega un centesimo di secondo invece che qualche nanosecondo), senza incorrere nei problemi del tuo schema, tra cui:
    - necessità di storage aggiuntivo (inutile) per memorizzare gli hash "farlocchi" necessari per ottenere un determinato slowdown;
    - (improbabile) potenziale collisione di password di un utente con quelle di un altro;
    - cambiare la password senza sapere la precedente (ad esempio, per un reset password) aggiunge "spazzatura" al DB che non potrai mai rimuovere, dato che (by design) non puoi sapere quali hash sono buoni e quali sono farlocchi.
    Amaro C++, il gusto pieno dell'undefined behavior.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.