El problema principal: si su servidor puede comparar de manera eficiente una contraseña con hash con (potencialmente) todas las contraseñas con hash para todos los usuarios, entonces un atacante también puede hacerlo. Un atacante que tome una copia de los archivos / base de datos de su servidor estará en posición de ejecutar un ataque de diccionario sin conexión, es decir, crear una contraseña potencial y buscar una coincidencia.
El hash de contraseña normal utiliza un sal por contraseña para evitar ataques paralelos: queremos que el atacante pague el precio computacional completo de la función de hash (que se encarece en muchas iteraciones) para cada contraseña y cada cuenta de usuario. Sin embargo, si varios hashes de contraseña utilizan el mismo salt, entonces el atacante puede probar una contraseña potencial contra todos ellos por el costo de una invocación de función hash. Por lo tanto, su "sal global" debilita sustancialmente el esquema: le permite al atacante atacar 1000 cuentas por el costo de atacar una.
El punto importante: su problema es uno de temporalidad. De hecho, realmente no desea comparar una nueva contraseña de usuario con todas las contraseñas de todos los demás usuarios; lo que desea es comparar la contraseña elegida por cada usuario en el momento del registro con una lista limitada de "contraseñas de delincuentes conocidos". Desafortunadamente, cuando un usuario registrado entra en estado de "delincuente", ya está registrado y su contraseña no se ha mantenido, solo el hash. Entonces, realmente, le gustaría poder acceder a la contraseña utilizada para el registro después de el registro se ha realizado.
Una posible solución: usar custodia, también conocida como encriptación asimétrica . Crear un par de claves RSA. Almacena la clave pública en el servidor. NO almacene la clave privada correspondiente en el servidor; en cambio, guárdelo en otro lugar, por ejemplo en una computadora portátil que se mantiene fuera de línea (o tal vez solo en algunas unidades flash USB).
Cuando un usuario se registra, hash su contraseña como de costumbre (con PBKDF2, muchas iteraciones, una nueva sal aleatoria, etc.). Pero, también, cifre la contraseña (no el hash) con la clave RSA, y almacene el resultado del cifrado en su base de datos, junto con el hash. El cifrado solo necesita la clave pública, y es aleatorio, por lo que esta versión cifrada no le da un impulso adicional a un atacante que obtiene una copia del contenido de la base de datos. Cuando un usuario inicia sesión, se utiliza el hash de contraseña, como de costumbre.
Cuando un usuario resulta ser un spammer, obtenga la clave privada y descifre la contraseña en custodia. De esa manera, obtiene la "contraseña incorrecta" y puede agregarla a la lista de contraseñas para rechazarla al registrarse. Esa lista se puede mantener como texto claro: ya que las cuentas correspondientes se han cerrado, no hay problema con eso. Tenga cuidado de hacer el descifrado en una máquina "segura", preferiblemente fuera de línea: realmente no desea ver que la clave privada sea robada.
Una advertencia: los spammers son como las bacterias, ya que tienden a evolucionar con respecto a las restricciones externas. Si filtra a los spammers basándose en su costumbre de reutilizar contraseñas para el registro, pronto los capacitará para generar contraseñas aleatorias. Por lo tanto, predigo que si instala un sistema de este tipo, dejará de ser efectivo para expulsar a los spammers después de un tiempo relativamente corto; después de eso, solo tendrá un peso muerto en su base de datos (no mucho, porque un mensaje corto cifrado RSA con una clave RSA de 2048 bits es solo de 256 bytes, pero el peso muerto es igual).