Archiviazione sicura dei valori crittografati che consente ricerche DB?

4

Sto sviluppando un sistema che deve memorizzare i numeri di identificazione nazionali finlandesi nel modo più sicuro possibile pur consentendo "questo numero identificativo esiste nel sistema?" - digita le query su un'API Web.

A prima vista, la risposta potrebbe sembrare ovvia: memorizzare un hash salato del numero, in modo che quando viene fornita una query API contenente un numero ID non crittografato, vedere se la sua versione salata e con hash esiste nel database, come fai con le password.

Tuttavia, mentre questo approccio potrebbe funzionare abbastanza bene con le password, non è altrettanto semplice con i numeri ID, perché ce ne sono così pochi possibili. Il loro formato è DDMMYY [- + A] XXXZ. Per ogni anno AA hai 365 o 366 possibili combinazioni DDMM. Per ogni combinazione DDMMYY hai un numero sequenziale XXX, da 002 a 899. Z è un carattere di checksum calcolato dalle nove cifre precedenti. Il separatore - / + / A tra la data e la sequenza numerica denota il secolo di nascita.

Se un utente malintenzionato ottiene l'accesso al server, ha accesso al codice sorgente Python e al database e può quindi vedere immediatamente come viene eseguita la salatura e l'hashing dei numeri ID. Dato il numero limitato di possibili numeri ID (che possono essere ulteriormente ridotti con la conoscenza demografica delle persone memorizzate nel database), è banale generare tutti gli hash salati possibili per ogni possibile numero di identificazione, per ogni persona nel database.

Ho preso in considerazione la crittografia a chiave pubblica, ma non riesce perché la crittografia di un valore con una chiave pubblica in due occasioni diverse produce due diversi testi cifrati, quindi non possono essere utilizzati nelle ricerche come può fare l'hash.

Mi manca qualcosa di ovvio, o non c'è davvero alcun modo ragionevolmente sicuro in cui archiviare i numeri ID che potrebbero essere usati per le ricerche E che potrebbero resistere a una violazione del server, dove gli hash e il codice sorgente con cui sono stati creati fine nelle mani sbagliate?

    
posta JK Laiho 22.04.2015 - 13:56
fonte

2 risposte

1

Ho affrontato un problema simile, per il quale ho implementato la seguente mitigazione. Apprezzerei molto l'input poiché non sostengo che questa sia necessariamente una soluzione eccellente.

TL; DR

Archivia ID come HMAC(ID, master-key) e la password di ciascun utente viene utilizzata per ricavare una chiave per utente per crittografare master-key .

Capacità / limitazioni dell'avversario

Protegge contro un avversario in grado di leggere tutti i dati sul resto, ma non uno che può leggere la memoria (ad esempio intercettare le comunicazioni tra processi tra il server Web e il CGI).

I sali one-to-one ostacolano la ricerca

La ricerca efficiente nel tuo caso richiede l'indicizzazione e la ricerca di un valore singolo . Salando ogni numero ID devi calcolare HASH(search query, salt) per tutti i sali che è (a) inefficiente quando fatto per ogni ricerca, e (b) inefficace dal punto di vista della sicurezza come descritto di seguito.

Spazio di ricerca minuscolo

Il problema principale sta nel fatto che lo spazio di ricerca dei numeri ID è così piccolo. Gli attacchi di forza bruta sugli hash sono, oltre ad altri mezzi, mitigati dall'aumento dei fattori di lavoro (aka stretching) con PBKDF2, bcrypt, scrypt, ecc. Tuttavia lo spazio di ricerca è in realtà così piccolo che qualsiasi -un aumento in aumento del fattore lavoro per gli avversari sarebbe troppo scomodo per gli utenti (es. 30s + per ricerca).

Modifica il problema

Il mio approccio era di modificare il problema in uno di proteggere una chiave segreta. HMAC consente sia i dati (il numero ID), sia una chiave. Gli ID sono memorizzati come HMAC(ID, key) mentre le ricerche vengono eseguite con HMAC(search, key) . Ciò richiede corrispondenze esatte, ma può essere reso insensibile alle maiuscole / minuscole con uppercase(ID) e uppercase(search) . Un attacco a forza bruta con una chiave a 256 bit è impossibile anche senza un fattore di lavoro maggiore.

Come proteggiamo la chiave? Gli utenti di Query-API sono autenticati, quindi la loro password può essere utilizzata (dopo la salatura e l'allungamento) come "input keying material" (IKM) per HKDF . HKDF consente la generazione di chiavi indipendenti da una singola sorgente di entropia (IKM) includendo "informazioni contestuali" e una "salt". Per la funzione HKDF(IKM, context, salt) dove IKM = PBKDF2(password, rounds) calcoliamo quindi HKDF(IKM, 'Authentication', 'user-specific non-secret auth salt') e HKDF(IKM, 'KeyWrapping', 'user-specific non-secret wrap salt') . Il primo è memorizzato nel database in modo molto simile a un normale hash di autenticazione della password e quest'ultimo viene utilizzato per avvolgere (cioè crittografare) la chiave principale utilizzata negli HMAC precedenti (si noti che ogni utente ha la propria chiave master avvolta).

Ogni volta che una sessione viene autenticata, la chiave master non imballata viene racchiusa all'interno di una sessione crittografata. È possibile utilizzare HKDF con l'ID di sessione (già segreto) per derivare chiavi di crittografia / archiviazione per la sessione. A differenza delle password che devono essere allungate (con ritardo di un secondo o due dopo l'autenticazione), gli ID di sessione possono essere generati da un CSPRNG (assicurarsi di utilizzare HttpOnly e secure flags).

Questo solleva un altro problema in quanto è necessario inizialmente avvolgere la chiave principale per ogni utente senza conoscere realmente la propria password. Dopo aver creato l'utente nel database dovrebbero avere una coppia di chiavi privata / pubblica creata. La chiave privata è crittografata con HKDF(IKM, 'PrivateKeyEncryption', 'user-specific non-secret PKI salt') . A ciascun utente può quindi essere assegnata la chiave master da un utente amministrativo. Al momento dell'accesso al sistema controlla la loro inbox pubkey per ogni nuova chiave master e la avvolge per la prossima volta. In realtà potresti semplicemente utilizzare l'approccio asimmetrico, ma è computazionalmente più costoso.

Avvertimenti

  • È impossibile ripristinare le password perse senza richiedere all'utente amministrativo di fornire la chiave master all'utente.
  • NON sono un crittografo - potrebbero esserci vulnerabilità risultanti dall'avere più istanze di testo cifrato dello stesso testo in chiaro (EDIT: vedere il primo commento). In tal caso, può essere mitigato usando prefissi e suffissi entropici di lunghezza casuale? O è sufficiente una IV? Potrebbero esserci altri problemi che ho completamente trascurato.
  • Questo si basa su chiavi segrete effimere (password al momento del login, ID di sessione su ogni richiesta) che vengono archiviate in memoria. Non sono sicuro di come funzionerà il tuo cron job; una possibile soluzione consiste nell'utilizzare la crittografia asimmetrica con conoscenza di una chiave pubblica la cui chiave privata è crittografata con la chiave principale a cui tutti gli utenti possono accedere (qualsiasi sessione autenticata può quindi spostare le chiavi nel database).
  • Il tuo modello di minaccia richiede una progettazione così ampia? La complessità genera più posti in cui le cose vanno male.
risposta data 29.04.2015 - 05:30
fonte
0

Se un utente malintenzionato ottiene il tuo codice Python, entrambe le opzioni (usa salt + hash o crypto della chiave pubblica) non sono abbastanza sicure ...

Ma quando dici:

I considered public key crypto, but that fails because encrypting a value with a public key on two different occasions produces two different ciphertexts, so they can't be used in lookups like hashes can.

È vero solo se firmi contenuti diversi (perché il tuo sale), ma perché aggiungere sale allora? perché non firmare con la chiave pubblica il proprio hash (senza sale) e delegare la sicurezza alla crittografia a chiave pubblica? I contenuti chipered generati saranno sempre gli stessi, ma saranno garantiti con un strong metodo chiper (strong come si sceglie). Ma ancora una volta, se un utente malintenzionato ottiene la tua chiave privata, sei anche di proprietà di ...

Se non mi manca un meccanismo, penso che se nessun segreto privato viene tenuto al sicuro (il tuo codice Python, la tua chiave privata ...) chiunque può generarlo come te.

    
risposta data 22.04.2015 - 14:21
fonte

Leggi altre domande sui tag