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.