Hash y salt seguros para contraseñas PHP
Actualmente se dice que MD5 es parcialmente inseguro. Teniendo esto en cuenta, me gustaría saber qué mecanismo utilizar para la protección con contraseña.
Esta pregunta: ¿Es el “doble hash” una contraseña menos segura que simplemente hacerlo una vez? sugiere que el hash varias veces puede ser una buena idea, mientras que ¿ Cómo implementar protección con contraseña para archivos individuales? sugiere usar sal.
Estoy usando PHP. Quiero un sistema de cifrado de contraseñas rápido y seguro. Aplicar hash a una contraseña un millón de veces puede ser más seguro, pero también más lento. ¿Cómo lograr un buen equilibrio entre velocidad y seguridad? Además, preferiría que el resultado tuviera una cantidad constante de caracteres.
- El mecanismo de hash debe estar disponible en PHP.
- debe ser seguro
- Puede usar sal (en este caso, ¿todas las sales son igualmente buenas? ¿Hay alguna forma de generar buenas sales?)
Además, ¿debería almacenar dos campos en la base de datos (uno usando MD5 y otro usando SHA, por ejemplo)? ¿Lo haría más seguro o inseguro?
En caso de que no haya sido lo suficientemente claro, quiero saber qué funciones hash usar y cómo elegir una buena sal para tener un mecanismo de protección con contraseña rápido y seguro.
Preguntas relacionadas que no cubren del todo mi pregunta:
¿Cuál es la diferencia entre SHA y MD5 en PHP
Cifrado de contraseña simple
Métodos seguros para almacenar claves y contraseñas para asp.net
¿Cómo implementaría contraseñas saladas en Tomcat 5.5?
DESCARGO DE RESPONSABILIDAD : Esta respuesta fue escrita en 2008.
Desde entonces, PHP nos ha brindado
password_hash
ypassword_verify
, desde su introducción, son el método recomendado de verificación y hash de contraseñas.Sin embargo, la teoría de la respuesta sigue siendo una buena lectura.
TL;DR
No hacer
- No limite los caracteres que los usuarios pueden ingresar como contraseñas. Sólo los idiotas hacen esto.
- No limite la longitud de una contraseña. Si sus usuarios quieren una oración con supercalifragilisticexpialidocious, no les impida usarla.
- No elimine ni escape el HTML ni los caracteres especiales de la contraseña.
- Nunca almacene la contraseña de su usuario en texto sin formato.
- Nunca envíe por correo electrónico una contraseña a su usuario , excepto cuando haya perdido la suya y usted le haya enviado una temporal.
- Nunca jamás registres contraseñas de ninguna manera.
- ¡Nunca haga hash de contraseñas con SHA1 o MD5 o incluso SHA256! Los crackers modernos pueden superar los 60 y 180 mil millones de hashes/segundo (respectivamente).
- No mezcle bcrypt y con la salida sin formato de hash() , use salida hexadecimal o base64_encode. (Esto se aplica a cualquier entrada que pueda tener un deshonesto
\0
, lo que puede debilitar seriamente la seguridad).
dos
- Utilice scrypt cuando pueda; bcrypt si no puedes.
- Utilice PBKDF2 si no puede utilizar ni bcrypt ni scrypt, con hashes SHA2.
- Restablezca las contraseñas de todos cuando la base de datos esté comprometida.
- Implemente una longitud mínima razonable de 8 a 10 caracteres, además requiera al menos 1 letra mayúscula, 1 letra minúscula, un número y un símbolo. Esto mejorará la entropía de la contraseña, lo que a su vez hará que sea más difícil de descifrar. (Consulte la sección "¿Qué hace que una contraseña sea buena?" para conocer un poco de debate).
¿Por qué hash de contraseñas de todos modos?
El objetivo detrás del hash de contraseñas es simple: evitar el acceso malicioso a las cuentas de los usuarios comprometiendo la base de datos. Entonces, el objetivo del hash de contraseñas es disuadir a un hacker o cracker costándole demasiado tiempo o dinero calcular las contraseñas en texto plano. Y el tiempo y el costo son los mejores elementos disuasorios de su arsenal.
Otra razón por la que desea un hash bueno y sólido en las cuentas de usuario es para tener tiempo suficiente para cambiar todas las contraseñas del sistema. Si su base de datos está comprometida, necesitará tiempo suficiente para al menos bloquear el sistema, si no, cambiar todas las contraseñas de la base de datos.
Jeremiah Grossman, CTO de Whitehat Security, declaró en el blog de White Hat Security después de una reciente recuperación de contraseña que requirió romper con fuerza bruta su protección de contraseña:
Curiosamente, al vivir esta pesadilla, aprendí MUCHO que no sabía sobre descifrado, almacenamiento y complejidad de contraseñas. He llegado a comprender por qué el almacenamiento de contraseñas es mucho más importante que la complejidad de las contraseñas. Si no sabe cómo se almacena su contraseña, entonces lo único en lo que realmente puede confiar es en la complejidad. Esto puede ser de conocimiento común para los profesionales de contraseñas y criptografía, pero para el experto promedio en InfoSec o Seguridad Web, lo dudo mucho.
(El énfasis es mío).
¿ Qué hace que una contraseña sea buena ?
Entropía . (No es que suscriba totalmente el punto de vista de Randall).
En resumen, la entropía es cuánta variación hay dentro de la contraseña. Cuando una contraseña consta solo de letras romanas minúsculas, son solo 26 caracteres. Esa no es mucha variación. Las contraseñas alfanuméricas son mejores, con 36 caracteres. Pero permitir mayúsculas y minúsculas, con símbolos, es de aproximadamente 96 caracteres. Eso es mucho mejor que solo letras. Un problema es que, para que nuestras contraseñas sean memorables, insertamos patrones, lo que reduce la entropía. ¡Ups!
La entropía de la contraseña se aproxima fácilmente. El uso de toda la gama de caracteres ascii (aproximadamente 96 caracteres que se pueden escribir) produce una entropía de 6,6 por carácter, que con 8 caracteres para una contraseña sigue siendo demasiado baja (52,679 bits de entropía) para la seguridad futura. Pero la buena noticia es: las contraseñas más largas y las contraseñas con caracteres Unicode realmente aumentan la entropía de una contraseña y la hacen más difícil de descifrar.
Hay una discusión más extensa sobre la entropía de contraseñas en el sitio Crypto StackExchange . Una buena búsqueda en Google también arrojará muchos resultados.
En los comentarios hablé con @popnoodles, quien señaló que aplicar una política de contraseñas de X longitud con X muchas letras, números, símbolos, etc., en realidad puede reducir la entropía al hacer que el esquema de contraseñas sea más predecible. Estoy de acuerdo. La aleatoriedad, lo más verdaderamente aleatoria posible, es siempre la solución más segura pero menos memorable.
Hasta donde he podido decir, crear la mejor contraseña del mundo es un callejón sin salida. O no es fácil de recordar, es demasiado predecible, es demasiado corta, tiene demasiados caracteres Unicode (difíciles de escribir en un dispositivo Windows/Mobile), es demasiado larga, etc. Ninguna contraseña es realmente lo suficientemente buena para nuestros propósitos, por lo que debemos protegerlas como si Estaban en Fort Knox.
Mejores prácticas
Bcrypt y scrypt son las mejores prácticas actuales. Scrypt será mejor que bcrypt con el tiempo, pero no ha sido adoptado como estándar por Linux/Unix o por servidores web, y aún no se han publicado revisiones en profundidad de su algoritmo. Pero aún así, el futuro del algoritmo parece prometedor. Si estás trabajando con Ruby, hay una gema scrypt que te ayudará y Node.js ahora tiene su propio paquete scrypt . Puede usar Scrypt en PHP a través de la extensión Scrypt o la extensión Libsodium (ambas están disponibles en PECL).
Le recomiendo encarecidamente leer la documentación de la función crypt si desea comprender cómo usar bcrypt, encontrar un buen contenedor o usar algo como PHPASS para una implementación más heredada. Recomiendo un mínimo de 12 rondas de bcrypt, si no de 15 a 18.
Cambié de opinión sobre el uso de bcrypt cuando supe que bcrypt solo usa la programación de claves de Blowfish, con un mecanismo de costo variable. Este último le permite aumentar el costo de forzar una contraseña mediante el aumento de la ya costosa programación de claves de Blowfish.
Prácticas promedio
Ya casi no puedo imaginar esta situación. PHPASS admite PHP 3.0.18 a 5.3, por lo que se puede utilizar en casi todas las instalaciones imaginables y debe usarse si no está seguro de que su entorno admita bcrypt.
Pero supongamos que no puede utilizar bcrypt o PHPASS en absoluto. ¿Entonces que?
Pruebe una implementación de PDKBF2 con el número máximo de rondas que su entorno/aplicación/percepción del usuario puede tolerar. El número más bajo que recomendaría es 2500 rondas. Además, asegúrese de utilizar hash_hmac() si está disponible para que la operación sea más difícil de reproducir.
Prácticas futuras
PHP 5.5 incluye una biblioteca completa de protección con contraseña que elimina cualquier molestia de trabajar con bcrypt. Si bien la mayoría de nosotros estamos atrapados con PHP 5.2 y 5.3 en los entornos más comunes, especialmente en hosts compartidos, @ircmaxell ha creado una capa de compatibilidad para la próxima API que es compatible con PHP 5.3.7.
Resumen y descargo de responsabilidad sobre criptografía
La potencia computacional necesaria para descifrar una contraseña hash no existe. La única forma que tienen las computadoras de "descifrar" una contraseña es recrearla y simular el algoritmo hash utilizado para protegerla. La velocidad del hash está linealmente relacionada con su capacidad de ser forzado de forma bruta. Peor aún, la mayoría de los algoritmos hash se pueden paralelizar fácilmente para funcionar aún más rápido. Por eso son tan importantes esquemas costosos como bcrypt y scrypt.
No es posible prever todas las amenazas o vías de ataque, por lo que debe hacer todo lo posible para proteger a sus usuarios desde el principio . Si no lo haces, es posible que incluso te pierdas el hecho de que fuiste atacado hasta que sea demasiado tarde... y seas responsable . Para evitar esa situación, para empezar, actúa de forma paranoica. Ataca tu propio software (internamente) e intenta robar las credenciales de los usuarios, modificar las cuentas de otros usuarios o acceder a sus datos. Si no prueba la seguridad de su sistema, no podrá culpar a nadie más que a usted mismo.
Por último: no soy un criptógrafo. Todo lo que he dicho es mi opinión, pero creo que se basa en el buen sentido común... y en mucha lectura. Recuerde, sea lo más paranoico posible, haga que las cosas sean lo más difíciles de entrometerse y luego, si todavía está preocupado, comuníquese con un hacker de sombrero blanco o un criptógrafo para ver qué dicen sobre su código/sistema.
Una respuesta mucho más simple y segura: no escriba su propio mecanismo de contraseña , utilice un mecanismo probado y comprobado:
Para PHP 8 (actual en 2023), use contraseña_hash() : buena calidad y parte del núcleo de PHP desde PHP 5.5.
La mayoría de los programadores simplemente no tienen la experiencia para escribir código relacionado con criptografía de forma segura sin introducir vulnerabilidades.
Autoprueba rápida
¿Qué es la extensión de contraseña y cuántas iteraciones debería utilizar?
Si no sabe la respuesta, debería usar password_hash()
, ya que la extensión de contraseñas es ahora una característica crítica de los mecanismos de contraseñas debido a CPU mucho más rápidas y al uso de GPU y FPGA para descifrar contraseñas a velocidades de miles de millones de conjeturas por segundo (con GPU ).
Ejemplo: descifrar contraseñas de Windows
A partir de 2023, puedes descifrar todas las contraseñas de Windows de 8 caracteres en 48 minutos utilizando 8 GPU. (Usando hashcat
288 mil millones de hashes/seg para contraseñas NTLM)
Esto es fuerza bruta, es decir, enumerar y verificar cada contraseña de Windows de 8 caracteres , incluidos los caracteres especiales, no un ataque de diccionario.
El ejemplo de 2023 es aproximadamente 7,5 veces más rápido que el de 2012, cuando se podían descifrar todas las contraseñas de Windows de 8 caracteres en 6 horas utilizando 25 GPU.
Ahora también puedes alquilar GPU en la nube durante unas horas a un coste razonable.
¿Por qué las contraseñas de Windows son tan descifrables?
Todo esto se debe a que Windows todavía no modifica ni amplía sus contraseñas, ni siquiera en Windows 10 u 11 , ya que necesita permitir inicios de sesión NTLMv1 .
Esto seguirá siendo cierto en 2023; consulte estas preguntas y respuestas .
¡No cometas los mismos errores que Microsoft!
Ver también
- Excelente respuesta con más información sobre por qué
password_hash()
o cuálphpass
es el mejor camino a seguir. - Buena publicación de blog que ofrece "factores de trabajo" recomendados (número de iteraciones) para los algoritmos principales, incluidos bcrypt, scrypt y PBKDF2.
PHP heredado
Para PHP muy antiguo (4.x): la biblioteca phpass de OpenWall era mucho mejor que la mayoría del código personalizado, utilizado en WordPress, Drupal, etc.