Come hash sicuro / tokenize una stringa

4

Un sistema su cui sto lavorando accetta come input un numero di conto cliente e deve generare un token basato su di esso. Non è consentito archiviare il testo in chiaro del numero di conto stesso, pertanto l'obiettivo del token è il seguente:

  1. Impossibile essere invertito nel numero di conto.
  2. Può essere usato per cercare e identificare in modo univoco un record di metadati dell'account nel nostro database.

I numeri di conto assomigliano ai numeri delle carte di credito. Sono lunghe stringhe numeriche a 16 cifre; i primi 4 caratteri sono costanti; l'ultimo carattere è una cifra di controllo calcolabile. Ciò significa che la dimensione effettiva dell'insieme di input è una stringa numerica di 11 caratteri: 99.999.999.999 possibili permutazioni.

Ho pensato ai metodi seguenti. Assumere hash significa sufficientemente lento hash sicuro quale alta iterazione PBKDF2, bcrypt o Argon2 e cifrare significa AES256.

1. Hash semplice

hash(account_num)

Pur essendo semplice, questo approccio è facilmente reversibile tramite la forza bruta ed è vulnerabile alle tabelle arcobaleno.

2. Hash salato per account

hash(salt + account_num)

Questo approccio corregge la vulnerabilità alle tabelle arcobaleno, tuttavia, a causa delle dimensioni limitate del set di input è ancora facile da annullare tramite brute-force.

3. Hash salato per utente crittografato con Global Pepper

encrypt(hash( salt + account_num ), pepper)

Questo è basato su di Dropbox memorizzazione delle password meccanismo . L'inversione tramite brute-force richiede la perdita sia dei blob crittografati che della chiave di crittografia. Tuttavia, poiché la crittografia dello stesso valore due volte con la stessa chiave genera diversi blob di output, ciò interrompe la possibilità di selezionare un account dal database per numero di account.

4. Approccio ibrido

  1. Memorizza le ultime 4 cifre del numero di conto in testo semplice.
  2. Memorizzato l'intero numero di conto come hash salato crittografato con pepe.
    1. Utilizza AWS KMS per la crittografia hash per ridurre le probabilità di perdita di una chiave.

Cosa questo compie:

  1. Possiamo cercare gli account utilizzando le ultime 4 cifre del numero di conto. Basandosi sul controllo di alcune migliaia di numeri di conto, questa selezione tornerà tra 1-3 possibili account.
  2. Fai scorrere su ciascuna delle possibili corrispondenze dell'account ...
    1. Decrittografa l'hash del numero di conto per l'account.
    2. Confronta l'hash decrittografato con il numero dell'account di input.
    3. Interrompi l'iterazione non appena troviamo un hash corrispondente (utilizza questa riga account) o esaurisci gli account (crea una nuova riga account).

La mia domanda: l'approccio 4 ha davvero senso? Per la sicurezza extra che fornisce, è eccessivamente complicato? Ha dei difetti a cui non ho pensato? Soprattutto, c'è un modo più semplice per risolvere questo problema?

    
posta crgwbr 28.02.2017 - 18:04
fonte

3 risposte

5

Sembra che il tuo numero di conto sia un numero di carta di credito. Convenientemente, so per esperienza che una scheda grafica vecchia di cinque anni può esaurire una chiave numerica a 10 cifre salata con sale in 3 giorni, ovvero una media di 36 ore per invertire un hash PBKDF (4096). Non ti sta già bene.

Stai molto meglio generando un nonce casuale da associare alla transazione e associare quel nonce con la carta.

    
risposta data 01.03.2017 - 00:01
fonte
1

Potresti semplicemente utilizzare un HMAC : gli HMAC hanno due input, un messaggio e una chiave di crittografia e producono un codice di autenticazione del messaggio, che è fondamentalmente un hash (ad esempio, non è possibile ripristinare il MAC nel messaggio, o la chiave, o entrambi). Quindi nel tuo caso avresti:

token = hmac(account_num, key)

HMAC è costruito in un modo speciale per proteggere dagli attacchi che potrebbero rivelare la chiave. È un primitivo crittografico ben compreso.

Senza conoscere la chiave, un attacco di forza bruta sul token per ottenere il numero di conto non è pratico. OTOH, per te, conoscendo la chiave, è banale trasformare un numero di conto in un token.

Ovviamente, ora la sicurezza dei numeri del tuo account dipende dalla sicurezza della chiave; quindi se perdi la chiave e l'elenco di token per un utente malintenzionato, hai aperto i tuoi numeri di account agli attacchi di forza bruta. Ma la tua idea numero 4 soffre dello stesso problema.

Potrebbe essere una buona idea pensare alla gestione delle chiavi, così puoi facilmente cambiare una chiave se ne viene compromessa.

    
risposta data 28.02.2017 - 23:44
fonte
1

Il tuo design non è sicuro contro un attacco oracolare. Qualsiasi numero di testo chiaro inserito nel sistema genererà lo stesso token generato. Al posto dell'hash di forza bruta, l'attaccante deve semplicemente usare il tuo sistema per fare la sua serie di ipotesi, e il tuo oracolo gli dirà se ha ragione o torto.

Hai chiamato questi "numeri di conto" senza definire nulla su come vengono assegnati. Se sono veramente casuali, allora sì, hai 10.000.000.000 di possibili ipotesi. Ma come sappiamo, il vero caso è difficile da trovare. E se non sono crittograficamente distribuiti a caso tra i possibili valori, un attaccante ottiene una culla enorme.

In primo luogo userò le carte di credito come un esempio reale di come le greppie consentano loro di essere interrotte, e quindi mostriamo come l'esempio potrebbe essere sfruttato in altri sistemi, forse i tuoi.

Considerare i numeri di conto emessi dalle banche. Ogni carta identifica la banca nelle prime sei cifre, che sono chiamate il numero di identificazione della banca (BIN). Statisticamente, i clienti avranno un mix di carte di credito, ma una percentuale significativa (diciamo il 13%) di queste è altamente probabile che sia stata emessa da banche locali al negozio in cui vengono utilizzate. Questa è la nostra culla. Quindi, se vedo il valore hash con una carta che termina con 1234, e so che il BIN della banca locale è 444444, semplicemente forza bruta forzare tutte le cifre mancanti in questa immagine: 4444 44?? ???# 1234 . Il carattere # utilizza l'algoritmo della cifra di controllo per recuperare una cifra mancante da un'ipotesi. Con solo 100.000 tentativi, avrò il 13% di possibilità di indovinare una carta valida nel tuo sistema.

Quindi estendiamo questo alle tue carte e hai un paio di venditori di carte che li stampano per te. Se effettui un ordine per 100.000 carte oggi e riordini 100.000 carte il mese prossimo, il venditore potrebbe non avere modo di sapere se la nuova corsa sta emettendo gli stessi numeri di carta della corsa precedente. Quindi, per evitare collisioni, inserisce un numero univoco di sei cifre come le prime sei cifre dei numeri delle carte, assicurando che ciascun lotto sia diverso da ogni altro lotto. (Questo è un comportamento molto comune per i fornitori che stampano buoni regalo.) Questo ha un effetto simile a quello che fa un numero BIN sul rendere i numeri indovinati. Ci sono circa 50.000 BIN rilasciati per le carte di credito, ma potrebbe essere peggio nel tuo caso perché non sappiamo quanti possibili gruppi di carte hai stampato.

Ricorda che un attaccante non sta cercando di bilanciare i libri; non ha bisogno della perfezione per avere successo come ladro. Non deve decifrare un numero di token specifico che trova per rubare a una persona. Tutto quello che deve fare è la forza bruta contro il tuo database, e trovare uno o più numeri che funzioneranno per lui. Più lui indovina con successo, più guadagna, ma qualsiasi successo con il furto delle carte è una vittoria per lui.

Considera invece un sistema di token monouso. Se l'attaccante inserisce 4444 4400 0001 1234 due volte, ottiene due diversi token dal sistema. Questo è l'unico modo per impedire al tuo sistema di fornire potenziali aggressori con un oracolo.

    
risposta data 01.03.2017 - 15:58
fonte

Leggi altre domande sui tag