Il problema principale: se il tuo server può confrontare in modo efficiente una password con hash con (potenzialmente) tutte le password con hash per tutti gli utenti, quindi anche un utente malintenzionato. Un utente malintenzionato che raccoglie una copia dei file / database del server sarà in grado di eseguire un attacco di dizionario offline, ovvero hashing password potenziale e cercare una corrispondenza.
L'hashing della password normale utilizza una password per salt per prevenire attacchi paralleli: vogliamo che l'attaccante paghi il prezzo computazionale completo della funzione hash (che è costosa in molte iterazioni) per ogni password e ogni account utente. Tuttavia, se diversi hash delle password utilizzano lo stesso salt, l'attacker può provare una potenziale password contro tutti per il costo di una chiamata di funzione hash. Quindi, il tuo "sale globale" indebolisce sostanzialmente lo schema: consente all'attaccante di attaccare 1000 account per il costo di attaccarne uno.
Il punto importante: il tuo problema è di temporalità. In effetti, in realtà non si desidera confrontare una nuova password utente con tutte le password di tutti gli altri utenti; ciò che si desidera è confrontare la password scelta da ciascun utente al momento della registrazione con un elenco limitato di "password dei trasgressori noti". Sfortunatamente, quando un utente registrato cade nello stato di "trasgressore", è già registrato e la sua password non è stata mantenuta in giro, solo l'hash di ciò. Quindi, davvero, ti piacerebbe poter accedere alla password utilizzata per la registrazione dopo la registrazione è avvenuta.
Una possibile soluzione: utilizzare l'impegno, alias crittografia asimmetrica . Creare una coppia di chiavi RSA. Archivia la chiave pubblica sul server. NON memorizzare la chiave privata corrispondente sul server; invece, memorizzalo altrove, ad es. su un laptop che è tenuto offline (o forse solo su alcune unità flash USB).
Quando un utente si registra, usa la sua password come al solito (con PBKDF2, molte iterazioni, un nuovo salt random, ecc.). Ma, anche, crittografare la password (non l'hash) con la chiave RSA e memorizzare il risultato della crittografia nel database, insieme all'hash. La crittografia richiede solo la chiave pubblica ed è randomizzata, quindi questa versione crittografata non fornisce ulteriore leva a un utente malintenzionato che ottiene una copia del contenuto del database. Quando un utente accede, l'hash della password viene utilizzato, come al solito.
Quando un utente risulta essere uno spammer, ottiene la chiave privata e decrittografa la password di deposito. In questo modo, ottieni la "password errata" e puoi aggiungerla all'elenco delle password da rifiutare al momento della registrazione. Tale elenco può essere mantenuto come testo chiaro: poiché i conti corrispondenti sono stati chiusi, non vi è alcun problema con questo. Fai attenzione a decifrare su una macchina "sicura", preferibilmente offline: non vuoi davvero vedere rubata la chiave privata.
Una parola di cautela: gli spammer sono come i batteri, in quanto tendono ad evolversi rispetto ai vincoli esterni. Se si escludono gli spammer in base alla loro abitudine di riutilizzare le password per la registrazione, verranno presto addestrate a generare password casuali. Quindi, prevedo che se si installa un tale sistema, cesserà di essere efficace nel respingere gli spammer dopo un tempo relativamente breve; dopo di ciò, sarà semplicemente un peso morto nel tuo database (non molto, perché un breve messaggio crittografato con RSA con una chiave RSA a 2048 bit è solo 256 byte, ma è comunque senza peso).