Teorico: salatura della password con concatenazione e salatura con HMAC

-1

Stavo osservando diversi metodi di salatura e ho cercato di confrontare quale è più sicuro per l'archiviazione delle password. So che HMAC non è stato concepito all'inizio per l'hashing della password ma è ampiamente utilizzato a tale scopo. So anche che il metodo più sicuro sta usando il key stretching. Ho esaminato CrackStation , vecchio non hash segreti articolo , domanda su questo argomento e 3 modi sbagliati per memorizzare le password articolo . Non ero ancora in grado di concludere quale dei seguenti metodi è più sicuro dell'altro e perché:

  • sha256 ($ pass. $ Sale)
  • sha256 ($ sale. $ Pass)
  • HMAC-SHA256 (chiave = $ pass, $ sale)
  • HMAC-SHA256 (chiave = $ sale, $ pass)

ATTENZIONE: nessuno di questi è sicuro per l'archiviazione delle password. La domanda riguarda le differenze teoriche marginali tra questi metodi di hashing della password e salatura.

Stavo considerando attacchi di estensione di lunghezza che probabilmente non sono un fattore qui o sono (che renderebbe HMAC più sicuro)? Allora forse HMAC sarebbe meno sicuro perché la chiave è nota? Come classificheresti questi metodi per sicurezza?

    
posta fsacer 24.04.2017 - 21:29
fonte

4 risposte

-1

Sulla base di ulteriori ricerche, suppongo che l'ordine sarebbe e conversazione su reddit :

(più sicuro)

  1. HMAC-SHA256 (chiave = $ sale, $ pass) - la cosa migliore è che non ha drawbac di limitazione della lunghezza della password, ma è anche favorevole perché la chiave è più casuale e potrebbe rendere l'hash ancora più casuale e non devi disinfettare la password in alcuni casi (grazie a @Nat)
  2. HMAC-SHA256 (chiave = $ pass, $ salt) - lunghezza password stabilmente limitata basato sull'implementazione di sha256-hmac, più sicuro a causa di un fattore di lavoro leggermente superiore
  3. sha256 ($ pass. $ salt) - basato su alcune specifiche dell'implementazione SHA, è più sicuro aggiungere sale
  4. sha256 ($ salt. $ pass) - il prepending sale non è sicuro

(meno sicuro)

Disclaimer: questa diffrazione è più di natura teorica e in pratica non si dovrebbe usare nessuno di questi metodi, ma usare uno qualsiasi di questi indicati nella risposta di Stephen.

    
risposta data 25.04.2017 - 21:22
fonte
2

(Nessuno degli esempi forniti deve essere utilizzato per l'hashing della password. Sono troppo veloci!)

Il primo principio generale che vorrei menzionare qui è che vogliamo che le nostre operazioni di crittografia siano interfacce di alto livello ; dovrebbe esserci una astrazione comune e riusabile che definisce:

  • Quali precondizioni deve soddisfare il chiamante prima di chiamarli;
  • Quali argomenti richiede l'operazione;
  • Quali valori restituisce o quali postcondizioni soddisfa quando viene chiamato correttamente;
  • Quali proprietà di sicurezza deve offrire il concetto.

In questo caso stiamo parlando di hashing password . Una funzione di hashing della password, nella sua più semplice incarnazione, dovrebbe:

  • Accetta due argomenti, una salt e una password;
  • Restituisce un codice hash per quella password / combinazione di sale;
  • consuma molte risorse in modo da rallentare notevolmente l'attacco di un utente malintenzionato, ma non tanto da un utente onesto

Quindi SHA-256 fallisce il primo e il terzo punto. Accetta solo un argomento ed è troppo veloce!

E in realtà, l'interfaccia sopra descritta non è forse nemmeno la migliore per l'hashing delle password. Un'interfaccia migliore è una coppia di funzioni scritte sopra l'hash della password (in pseudocodice Python-ish):

def generate_new_verification_code(password):
    salt = crypto_random(16)     # 16 byte random salt
    hash = password_hash(salt, password)
    verification_code = salt + hash
    return verification_code

def verify_password(putative_password, actual_verification_code):
    actual_salt = actual_verification_code[0:16]
    actual_hash = actual_verification_code[16:-1]
    putative_hash = password_hash(actual_salt, putative_password)
    return putative_hash == actual_hash

Quindi un'astrazione appropriata dice che per la verifica della password dovremmo usare due funzioni come queste, scritte sopra una funzione password_hash intensiva delle risorse, che a sua volta verrebbe probabilmente scritta su una funzione di basso livello come SHA -256.

Si noti che l'interfaccia di hashing della password di PHP segue effettivamente quest'ultimo design:

  • La funzione password_hash deve essere chiamata con una password ma senza sale ; seleziona saltato in modo casuale, e come dice la pagina: "L'algoritmo utilizzato, costo e sale vengono restituiti come parte dell'hash. Pertanto, tutte le informazioni necessarie per verificare l'hash sono incluse in esso."
  • La funzione password_verify viene utilizzata per verificare se una password putativa corrisponde alla stringa di verifica .

Secondo problema: un'idea chiave nella progettazione di protocolli di sicurezza o altri costrutti di sicurezza è che gli autori di attacchi spesso infrangono le regole e fanno in modo che il codice venga richiamato in modi e contesti inattesi . Ad esempio, quando le persone scrivono qualcosa del genere:

hash($salt.$pass)
hash($pass.$salt)

... spesso assumono implicitamente che salt sarà sempre la stessa lunghezza fissa, e non si fermerà a considerare se qualche attacker potrebbe essere in grado di:

  1. Trova un modo per "infrangere le regole" in modo che possano determinare la lunghezza di salt da variare tra più chiamate al codice;
  2. Trova un modo inaspettato per sfruttarlo a proprio vantaggio.

Ad esempio, potresti pensare che sia impossibile per un utente malintenzionato trovare una collisione per l'hash delle password in questo modo, ma in effetti se possono controllare salt e pass è banale farlo:

hash("0001"."Passsword1!")
hash("0001P"."asssword1!")

Questo potrebbe sembrare inverosimile, e beh, a dirti la verità, non posso davvero pensare a uno scenario in cui ciò potrebbe essere sfruttabile. Ma vorremmo davvero che la nostra sicurezza si fondasse su basi più solide di "Non riesco a pensare a un modo per rompere questo". Quindi, come filosofia generale, è più sicuro progettare le cose in modo tale che non possano sorgere ambiguità. In questo caso, vorremmo che fosse mantenuta la seguente regola:

  • Se abbiamo due password diverse con due diversi sali, gli input che forniamo alla funzione di hash di basso livello dovrebbero essere diversi.

Quindi questo significa che per produrre l'input che nutriamo con l'hash sottostante, dovremmo preferire combine con una iniettivo funzione - una funzione tale che combine(salt1, password1) == combine(salt2, password2) se e solo se salt1 == salt2 e password1 == password2 .

hash(combine($salt, $password))

Alcuni modi per farlo:

  • Applica uno degli input a una dimensione fissa e antepone l'altro. HMAC lo fa internamente per le chiavi.
  • Hash uno dei due input e anteponilo all'altro. HMAC esegue questa operazione per le chiavi più lunghe della dimensione di blocco della funzione di hash sottostante.
  • Inserisci un delimitatore univoco tra gli input (probabilmente richiede di evitare le occorrenze del delimitatore nei valori di input).

Funzioni di hashing della password dedicate come PBKDF2, bcrypt, scrypt e Argon2 aderiscono alla maggior parte di queste idee.

    
risposta data 27.04.2017 - 07:29
fonte
2

I quattro metodi che hai elencato sono essenzialmente equivalenti e sicuri ai fini delle password di hashing.

La costruzione HMAC di H(k1 ++ H(k2 ++ m)) (dove ++ è concatenazione) è progettata specificamente per prevenire attacchi contro MAC usati per autenticare i messaggi scritti da qualcuno che possiede la chiave segreta ( k1, k2 ; dove k1 = K ⊕ opad , k2 = K ⊕ ipad , dove opad=0x5c5c...5c e ipad=0x3636...36 ). A causa delle comuni funzioni di hash come md5, sha1, sha2 vulnerabili agli attacchi di estensione della lunghezza, non puoi usare una costruzione come H(k ++ m) come MAC, poiché un intercettatore che ha osservato un messaggio firmato m potrebbe utilizzare un'estensione di lunghezza attacco per rendere un MAC valido per un messaggio m' = m ++ tampered_msg . Allo stesso modo, la costruzione H(m ++ k) è anche insicura se un utente malintenzionato può generare una collisione (trova due messaggi H(m1) = H(m2) dove m1 e m2 sono allineati su un blocco) e poi se può ingannare qualcuno nella creazione di un MAC per m1 , può usarlo come MAC valido per m2 .

Tuttavia, essere suscettibili agli attacchi di estensione della lunghezza o di pre-scontro non è rilevante per l'uso di memorizzazione e convalida delle password degli utenti. Tutto ciò che è rilevante è la lunghezza dell'hash restituito, il fatto che il sale è unico per ogni password memorizzata nel sistema (quindi più password non vengono attaccate in parallelo, il tempo per calcolare l'hash (gli hash intrinsecamente lenti sono più sicuri contro la brutalità forza) e la forza della password.

Se utilizzo H(random_salt ++ password) con un hash vulnerabile agli attacchi di estensione della lunghezza, non esiste una vulnerabilità equivalente. A chi importa se qualcuno che non conosce la mia password ma ha visto l'hash potrebbe potenzialmente calcolare H(random_salt ++ password ++ tampered_pw) per qualsiasi cosa tampered_pw vogliono aggiungere alla mia password senza capire che cos'è password ?

Detto questo, la costruzione HMAC può essere un po 'più sicura in quanto chiama la funzione hash due volte, quindi teoricamente dovrebbe impiegare il doppio del tempo per la forza bruta; anche se tipicamente quando esegui più turni di hashing fai molte migliaia di round di hashing (che rendono il bruto forzante molte migliaia di volte più difficile) invece di solo 2 round di hashing.

    
risposta data 27.04.2017 - 08:41
fonte
1

HMAC, di per sé, non è un hash della password appropriato. Stai lavorando a un livello troppo basso per la tua competenza crittografica. Esistono già costrutti progettati appositamente per la verifica infallibile delle password; usali! Scegli uno dei bcrypt , scrypt o Argon2 .

    
risposta data 25.04.2017 - 01:27
fonte

Leggi altre domande sui tag