Al almacenar las contraseñas de los usuarios que necesita verificar (pero no usar como texto sin formato), el estado actual de la técnica es:
- hash la contraseña
- Use una sal
- Utilice una función hash lenta: bcrypt, scrypt, etc.
Esto proporciona la mejor protección posible contra un atacante que ha robado la base de datos de contraseñas. No resuelve otros problemas como phishing, malware, reutilización de contraseñas, etc.
Pero hay un problema restante: la función hash lenta puede permitir un ataque de denegación de servicio . Una sola solicitud quema una gran cantidad de CPU, lo que hace posible un DOS con un número relativamente pequeño de solicitudes simultáneas, lo que dificulta el uso de defensas como la limitación de IP.
Dada la mejora en el rendimiento de JavaScript en los navegadores, ahora puede tener sentido hacerlo en el navegador. Supongo que aquí el sitio está utilizando SSL, por lo que el JavaScript se entrega de forma segura. Si no está utilizando SSL, es poco probable que el uso de una función hash lenta sea una prioridad para usted. Hay al menos una implementación de JavaScript de bcrypt. Pero usar esto de una manera simple presentaría dos problemas:
- El cliente necesita obtener el salt del servidor. Esto introduce la latencia en el inicio de sesión y, a menos que se tenga cuidado, puede revelar si existe una cuenta de usuario en particular.
- Si el hashing se realiza exclusivamente en el cliente, se pierden los beneficios de almacenar hashes. Al atacante que haya robado los hashes de contraseña, simplemente puede iniciar sesión con los hashes.
Sin embargo, creo que hay soluciones aceptables para ambos problemas:
- La sal se puede generar como hash (server_salt + user_name), donde server_salt es un número aleatorio que es único para el servidor, público y el mismo para todos los usuarios. El hash resultante parece tener las propiedades requeridas de una sal.
- El servidor debe realizar una sola operación hash rápida en el hash que recibe. Como ejemplo: el servidor almacena SHA-256 (bcrypt (salt, contraseña)). El cliente envía bcrypt (sal, contraseña), luego el servidor aplica SHA-256 y verifica el hash. Esto NO permite que un atacante realice un ataque rápido de fuerza bruta sin conexión. Pueden hacer una fuerza bruta rápida de SHA-256 (contraseña) porque la contraseña tiene una cantidad limitada de entropía: 2 ^ 50 o 2 ^ 60 o menos. Pero un bcrypt (sal, contraseña) de 128 bits tiene entropía o 2 ^ 128, por lo que no pueden forzarlo fácilmente.
Entonces, ¿es este un enfoque razonable y seguro?
Soy consciente de los consejos generales para "no enrollar tu propio cripto". Sin embargo, en este caso, estoy tratando de resolver un problema que no se resuelve con criptografía comercial. Para obtener credibilidad básica, esto se ha visto en John Steven ( un experto reconocido en el campo) con un resultado positivo de un análisis "breve".