it-swarm.dev

HMAC - Perché non HMAC per l'archiviazione della password?

Nota bene: Sono consapevole che buona risposta per proteggere la memorizzazione delle password è scrypt oppure criptato . Questa domanda non è per l'implementazione nel software reale, è per mia comprensione.

Supponiamo che Joe Programmer abbia il compito di memorizzare in modo sicuro le password degli utenti finali in un database per un'applicazione web; o la memorizzazione di password su disco per l'accesso a un software. Molto probabilmente:

  1. Ottieni $password Dall'utente finale.
  2. Crea $nonce Come valore casuale di circa 64 o 128 bit.
  3. Crea $hash = SHA256($nonce$password) e archivia $nonce Insieme a $hash Nel database.

Domanda 1:

Perché il seguente non è sostanzialmente migliore di quanto sopra?

  1. Crea $long_string Una volta e una sola volta. Memorizzare questo come costante nel codice dell'applicazione. $long_string Potrebbe f.x. essere 2 kilobyte di caratteri casuali.
  2. Ottieni $password Dall'utente finale.
  3. Crea $mac = HMAC-SHA256($long_string)[$password] (ovvero crea un MAC usando la password dell'utente finale come chiave) e memorizza $mac Nel database.

Immagino che l'HMAC abbia i seguenti vantaggi?

  • Le collisioni sono meno frequenti?
  • È computazionalmente un po 'più costoso del semplice hashing? (Ma non vicino a Scrypt, ovviamente.)
  • Per avere successo con un attacco di forza bruta entro un tempo ragionevole, l'attaccante dovrebbe avere accesso a due cose: 1) il database, dove è memorizzato $mac, e 2) il codice dell'applicazione, in cui è memorizzato l'originale $long_string. È meglio di una funzione hash, in cui l'attaccante deve solo accedere al database?

Tuttavia, nessuno sembra suggerire di usare un HMAC, quindi devo fraintendere qualcosa?

Domanda due:

Quali sarebbero le implicazioni dell'aggiunta di un valore salt $nonce?

  1. Crea $long_string Una volta e una sola volta. Memorizzare questo come costante nel codice dell'applicazione.
  2. Ottieni $password Dall'utente finale.
  3. Crea $nonce Come valore casuale di circa 128 bit di grandi dimensioni.
  4. Crea $mac = HMAC-SHA256($long_string)[$nonce$password] e archivia $nonce E $mac Nel database.
46
Jesper M

Il punto essenziale è prevenire la condivisione dei costi di attacco: se un attaccante vuole attaccare due password, dovrebbe essere due volte più costoso rispetto all'attacco di una password.

Con la tua proposta (la tua "domanda 1"), due utenti con la stessa password finiranno per usare lo stesso MAC. Se un utente malintenzionato ha accesso in lettura al database, può "provare" le password (ricompilando il MAC) e cercare una corrispondenza nel database. Può quindi attaccare tutte le password in parallelo, per il costo di attaccarne una. Se tuo long_string è una costante hardcoded nel codice sorgente dell'applicazione, quindi tutte le istanze installate condividono questa costante e diventa utile (per l'attaccante) pre-calcolare un grande dizionario di coppie password-MAC, noto anche come "a Tavolo arcobaleno ".

Con un nonce (la tua "domanda 2") eviti la condivisione dei costi. Il nonce è generalmente noto come "sale". Il tuo long_string e l'uso di HMAC non ti compra molto qui (e non stai usando HMAC per quello per cui è stato progettato, tra l'altro, quindi sei su basi traballanti, crittograficamente parlando). Usare un sale è un'ottima idea (beh, no usare un sale è una pessima idea, almeno) ma fa solo metà del lavoro. È inoltre necessario disporre di una procedura di hashing lenta. Il punto qui è che il salt impedisce la condivisione dei costi, ma non impedisce di attaccare una singola password. Attaccare una password significa provare possibili password fino a quando non si abbina (è "l'attacco del dizionario") e, data l'immaginazione dell'utente medio, gli attacchi del dizionario tendono a funzionare: le persone semplicemente amore usano password che possono essere indovinato. La soluzione alternativa consiste nell'utilizzare un processo di hashing intrinsecamente lento, di solito ripetendo alcune migliaia di volte la funzione hash. L'idea è di rendere la verifica della password più costosa: far aspettare 1ms invece di 1µs non è un problema (l'utente non se ne accorgerà), ma renderà anche l'attacco del dizionario 1000 volte più costoso. Il tuo long_stringpuò per questo, a condizione che sia veramente lungo (non 2 kilobyte, piuttosto 20 megabyte).

HMAC può essere usato al posto di una funzione hash non elaborata per rafforzare un sistema di verifica della password, ma in una configurazione diversa. Dato un sistema che controlla le password con sali e funzioni hash iterate, è possibile sostituire la funzione hash con HMAC, usando una chiave segreta K . Questo impedisce gli attacchi del dizionario offline fintanto che puoi mantenere K segreto. Mantenere un valore segreto non è facile, ma è ancora più semplice mantenere un segreto a 128 bit K rispetto a un database completo.

35
Thomas Pornin

HMAC non ti compra molto qui, anche se immagino che funzioni come una funzione hash sostituibile (sostituibile cambiando la chiave). Comunque sembra solo eccessivo qui.

Tuttavia, il primo schema che proponi sopra fallisce catastroficamente se $ long_string viene conosciuto da un attaccante, cosa che probabilmente avverrà poiché il codice non è probabilmente considerato segreto (ed è generalmente una cattiva pratica fare affidamento sulla segretezza del codice comunque).

Dovresti usare il secondo schema poiché $ nonce è necessario per proteggerti dagli attacchi precompilati.

3
frankodwyer