Se i dati su cui stai confrontando non sono un segreto, allora non importa ciò che fai. Gli attacchi a tempo vengono utilizzati per apprendere informazioni a cui non si ha già accesso. Se hai già accesso, l'attacco non è necessario.
Se stai proteggendo un segreto, non importa se si tratta di una password o meno. Dovresti gestirlo allo stesso modo, con le stesse precauzioni. Per evitare i canali laterali di temporizzazione, ci si assicura che la lunghezza dell'input sia uguale alla lunghezza del valore memorizzato eseguendoli entrambi e eseguendo un confronto temporale costante. Naturalmente, è necessario memorizzare il valore hash del valore memorizzato nel database, in modo che non lo si ricalcoli ogni volta. Vedi pseudocodice sotto.
Pseudocodice per confronti a tempo costante:
bool compareToSecret(input/* input from user*/)
dbHash = getSecretFromDb()
inHash = hash(input); // sha256, bcrypt or whatever. Attacker
// knows how long this takes, but it
// doesn't get her anything
// at this point, both inhash and dbhash are the same length.
// the following loop is constant order time. Hashes are always the
// same length, and all values of the hashes are always compared
bool result = true
int len = strlen(inHash)
for( int i=0; i<len; i++ )
result &= (inHash[i] == dbHash[i])
return result
interruzione anticipata durante il confronto
L'interruzione anticipata durante il confronto è problematica, in quanto fornisce informazioni sul confronto e può essere utilizzata per imparare direttamente la password.
- L'attaccante prova i segreti della lunghezza 1. Uno di questi segreti impiegherà più di un millisecondo per tornare rispetto agli altri, dal momento che esiste un segreto di 1 lunghezza il cui primo carattere corrisponde al segreto memorizzato. L'attaccante apprende il primo carattere del segreto.
- L'attaccante prova 2 tutti i segreti dei personaggi con la prima lettera che inizia con il valore corretto noto. Uno di questi segreti sarà più veloce di alcuni millisecondi, dal momento che il secondo carattere confronta correttamente.
- L'attaccante si ripete, attaccando separatamente ciascun personaggio del segreto. Alla fine, l'attaccante apprende il segreto memorizzato.
Questo è più veloce di un attacco di forza bruta, perché l'attaccante può attaccare ogni personaggio separatamente invece dell'intero segreto in una sola volta.
interruzione anticipata: prima di confrontare
Abortire presto, come menzionato da Steven Tousset, fa trapelare la lunghezza del segreto. All'inizio questo non sembra molto, ma lascia che l'aggressore acceleri il suo attacco di ordini di grandezza. Diciamo che il sistema accetta i segreti della lunghezza 1-5. Mantenendolo molto semplice, il segreto è composto solo da cifre. Quindi, ci sono 10 + 100 + 1.000 + 10.000 + 100.000 = 111,110 valori possibili. Un attacco di forza bruta deve fare circa 100.000 test per provare ogni possibile valore.
Se il sistema consente all'autore dell'attacco di conoscere la lunghezza del segreto, un utente malintenzionato può ridurre drasticamente il numero di valori che è necessario testare prima provando 5 valori: "1", "12", "123", "1234" e "12345". Ora l'attaccante sa per quanto tempo è il segreto. Se è 1 personaggio, ora deve solo fare 10 test invece di centomila. Se è lungo 2 caratteri, ha solo bisogno di fare 100 test. Senza questa informazione chiave, l'hacker deve fare molti più test prima di trovare il segreto.
un altro problema: spazio di archiviazione recuperabile
C'è anche un altro problema. Se il segreto conosciuto può essere confrontato con il valore di input, è necessario memorizzare il segreto in forma recuperabile. Cerca questo sito per i dettagli relativi alle password, ma questo è spesso considerato una cattiva pratica con l'archiviazione delle password e dovrebbe essere evitato. In generale, con qualsiasi tipo di segreto, se non si deve assolutamente avere il valore grezzo, non dovrebbe essere memorizzato in forma recuperabile. Lo storage irrecuperabile impedisce diversi attacchi, indipendentemente dal fatto che siano importanti per il tuo segreto, dipende da come lo usi.
un'altra cosa: ottimizzazione del compilatore
Il codice, sotto, è pseudo codice. Se dovessi passare il suo equivalente tramite un compilatore o un runtime che esegue l'ottimizzazione, devi essere molto attento a garantire che il codice non venga ottimizzato. Esistono vari trucchi per l'apprendimento di lingue diverse, che non rientrano nell'ambito di questo commento. Da ks a @Polynomial per ricordare questo