Contraseñas en la mira: por qué los métodos antiguos ya no funcionan
\nImagine: usted es un desarrollador que escribe un sistema de registro. ¿Parece que podría ser más sencillo? Toma la contraseña, la pasa por md5, la guarda en la base de datos. Pero en 2025, este enfoque es un camino directo a la vulnerabilidad. Los ataques modernos, el GPU-bruteforce y las tablas rainbow convierten los hashes débiles en polvo en segundos. La pregunta de Stack Overflow con miles de visitas — «Secure hash and salt for PHP passwords» — no pierde vigencia, y la resolveremos de una vez por todas.
\nEn este artículo aprenderá cómo hashear contraseñas correctamente en PHP, utilizando algoritmos modernos, funciones integradas y sal (salt). Adaptaremos las mejores respuestas de la comunidad para contenido educativo y mostraremos cómo proteger a los usuarios incluso en 2025.
\n\nPor qué md5 y sha1 son malos
\nSi todavía usa md5 o sha1 para almacenar contraseñas — deténgase. Estos algoritmos fueron creados para velocidad, no para seguridad. En 2025, una GPU puede generar miles de millones de hashes md5 por segundo. Agregar sal (una cadena aleatoria) no salva: las tablas rainbow se evitan, pero la velocidad sigue siendo fatal.
\n- \n
- Md5: 2^128 variantes — se descifra en minutos. \n
- Sha1: un poco mejor, pero también obsoleto. \n
- Sal sin ralentización: si el hash es rápido, la sal solo retrasa el ataque. \n
En su lugar, se necesitan algoritmos con «ralentización» — aquellos que requieren tiempo y memoria. Estos son los que usa password_hash.
password_hash y password_verify: solución integrada de PHP
\nDesde PHP 5.5 aparecieron dos funciones que resuelven el 90% de los problemas de seguridad de contraseñas: password_hash y password_verify. Generan automáticamente una sal criptográficamente segura y utilizan algoritmos resistentes al bruteforce. En 2025, esto es el estándar, ¡y nada de soluciones caseras!
¿Cómo funciona?
\nLa función password_hash toma una contraseña y un algoritmo (por defecto — bcrypt, pero se puede especificar argon2). Devuelve una cadena que ya contiene la sal y los parámetros. No necesita almacenar la sal por separado — está dentro del hash.
<?php\n// Hasheamos la contraseña\n$password = "SuperSecret123!";\n$hash = password_hash($password, PASSWORD_DEFAULT);\n\necho $hash; // $2y$10$... (60 caracteres)\n?>\n\nAhora verificamos la contraseña al iniciar sesión:
\n<?php\n$inputPassword = "SuperSecret123!";\n$storedHash = "$2y$10$..."; // de la base de datos\n\nif (password_verify($inputPassword, $storedHash)) {\n echo "¡Contraseña correcta!";\n} else {\n echo "Contraseña incorrecta.";\n}\n?>\n\nTodo es genialmente simple. password_verify extrae automáticamente la sal del hash y verifica la contraseña. Sin acciones adicionales.
Elección del algoritmo: bcrypt vs argon2
\nEn 2025 tiene dos opciones principales:
\n- \n
- Bcrypt (PASSWORD_BCRYPT): probado por el tiempo, se usa por defecto. Resistente a ataques, pero requiere configurar el cost factor. \n
- Argon2 (PASSWORD_ARGON2I o PASSWORD_ARGON2ID): ganador del concurso Password Hashing Competition. Más moderno, requiere más memoria, lo que dificulta los ataques con GPU. Recomendado para nuevos proyectos. \n
Ejemplo con argon2:
\n<?php\n$hash = password_hash($password, PASSWORD_ARGON2ID, [\n 'memory_cost' => 1 << 17, // 128 MB\n 'time_cost' => 4,\n 'threads' => 2\n]);\n?>\n\nEl parámetro memory_cost — cantidad de memoria en kilobytes. Cuanto más alto, más seguro, pero más lento. Para producción, pruebe en su servidor.
Sal (salt): cómo funciona y por qué no se necesita por separado
\nMuchos principiantes preguntan: «¿Cómo agregar la sal manualmente?». Respuesta: de ninguna manera. password_hash genera 22 caracteres de sal aleatoria (para bcrypt).