Confronto di stringhe per il cronometraggio in lingue di alto livello

1

Sto cercando una soluzione affidabile per confrontare due stringhe senza perdere il loro contenuto attraverso le differenze di tempo. La lunghezza delle stringhe è non secret.

Lo sfondo è questo: sto implementando l'autenticazione basata su password per un'applicazione web. Le password vengono sottoposte a hash con bcrypt e archiviate in un database. Piuttosto che dare all'applicazione l'accesso diretto agli hash in modo che controlli la password, vorrei delegare questo al sistema di database stesso. L'applicazione esegue solo l'hash della password e quindi passa l'hash a una procedura di database. Questa procedura ha privilegi speciali per accedere agli hash memorizzati e confrontarli con l'hash fornito. L'obiettivo è proteggere gli hash dagli attacchi SQL injection. Un'idea simile è descritta in questa presentazione .

Ovviamente, questo schema è efficace solo se la procedura non perde alcuna informazione sugli hash memorizzati. Quindi il confronto delle stringhe deve essere temporizzato.

Sono a conoscenza del seguente metodo (pseudo-codice):

string_comp(str_1, str_2):
    if str_1.length != str_2.length:
        return false
    else:
        result := 0
        for i := 0 to str_1.length - 1:
            result := result | (str_1[i] ^ str_2[i])

        return result == 0

Tuttavia, questo è piuttosto ingombrante e non sono sicuro che funzioni come previsto in linguaggi di alto livello come SQL.

Un altro suggerimento comune è quello di cancellare le stringhe e quindi confrontare il hash . Questo sarebbe molto più semplice del codice sopra.

Quale metodo è preferibile? Se dovessi usare la soluzione hash, quale algoritmo sceglierei per non degradare la forza di bcrypt (nemmeno teoricamente)? SHA-256? SHA-384? SHA-512?

    
posta Fleche 11.06.2014 - 00:34
fonte

2 risposte

2

Notare che non è possibile calcolare l'hash bcrypt nell'applicazione e passarlo al database a meno che non si memorizzino i sali separatamente dagli hash. Se lo fai, dovrai sostenere l'overhead di eseguire prima un SELECT, poi il bcrypt e poi il passo di verifica della password. Fare come suggerisce la presentazione (avere il DB calcolare l'hash) potrebbe avere più senso. Il modulo di crittografia di Postgres 9 supporta bcrypt come un tipo di hash incorporato.

Se si desidera eseguire comunque l'hashing nell'applicazione, il confronto con un hash è probabilmente il più semplice (con meno probabilità di avere problemi di implementazione). Poiché bcrypt è basato su blowfish, considera solo fino a 448 bit di input, quindi puoi tranquillamente eseguire l'hash dell'output con SHA-512 e matematicamente non perderà alcuna forza. In realtà, anche l'SHA-256 va bene, poiché le password degli utenti non contengono essenzialmente più di 256 bit di entropia. Perfino 256 bit di caratteri stampabili perfettamente casuali richiedono password di 56 caratteri. Il confronto usando un hash è probabile che abbia l'implementazione più semplice e dato che hai già il sovraccarico di bcrypt, il costo di 2 hash aggiuntivi è banale.

(Questa parte della risposta si basa su un'interpretazione errata della domanda come preoccupata per gli attacchi temporali contro l'applicazione, non per il database, ma lo lascio qui per riferimento.)

Se stai usando bcrypt, hai già cancellato la password, quindi non devi preoccuparti dei risultati dei tempi. Anche se qualcuno potrebbe dedurre l'hash della password, avrebbero comunque bisogno di trovare un testo in chiaro da fornire all'applicazione, che dovrebbe essere computazionalmente non fattibile (o bcrypt è rotto).

Gli attacchi a tempo sono utili solo se l'attaccante può scorrere i valori confrontati per trovare un utile confronto. (cioè A, B, C ...) Con un valore hash, questo non è possibile, poiché non possono scorrere l'output dell'hash (per hash crittograficamente forti). Piccole modifiche nell'input a bcrypt danno grandi cambiamenti all'output. Un utente che fornisce l'input per bcrypt non ha modo di provare i prefissi e quindi estendere il prefisso. Un utente malintenzionato non saprebbe nulla su entrambi i lati del confronto e, pertanto, gli attacchi di sincronizzazione non generano alcuna informazione sul valore dell'hash.

    
risposta data 11.06.2014 - 00:51
fonte
1

Rather than giving the application direct access to the hashes so that it check the password, I'd like to delegate this to the database system itself.

Perché vuoi delegarlo? È più semplice recuperare l'hash per il nome utente dato e fare un confronto costante tra stringhe laterali (usando lo pseudo-codice sopra) con l'hash recuperato.

Ogni accesso riuscito darà all'applicazione web in esecuzione sul server sia la password inserita che l'hash della password calcolata.

Certo, quello che vuoi è abbastanza semplice da fare all'interno di un determinato database usando il suo linguaggio procedurale (PL). Ad esempio, in postgres potresti scrivere:

CREATE OR REPLACE FUNCTION user_with_correct_hash(username TEXT, app_hash TEXT) RETURNS BOOLEAN AS
$$
DECLARE
    user_hash TEXT;
BEGIN
    user_hash := SELECT hash FROM users WHERE "name" = username;
    IF (user_hash IS NULL) THEN
        RETURN false;
    END IF;
    RETURN constant_time_string_compare(user_hash, app_hash);
END
$$
LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION constant_time_string_compare(user_hash TEXT, app_hash TEXT) RETURNS BOOLEAN AS
$$
DECLARE
    result INTEGER;
    index INTEGER;
    hash_length INTEGER;
BEGIN 
    hash_length := CHAR_LENGTH(app_hash);
    IF CHAR_LENGTH(user_hash) != hash_length THEN
        RETURN false;
    END IF;
    index := 1;
    result := 0;
    WHILE index <= hash_length LOOP
        result := result | (ascii(substr(app_hash, index, 1)) # ascii(substr(user_hash, index, 1)));
        index := index + 1;
    END LOOP;
    RETURN (result = 0);
END
$$
LANGUAGE plpgsql;

Bisogna stare molto attenti a dare il confronto costante tra stringhe di tempo e diversi casi di test per accertarsi che si comporti correttamente.

    
risposta data 11.06.2014 - 01:16
fonte

Leggi altre domande sui tag