Guías de seguridad
Cómo guardar contraseñas con seguridad — la forma correcta de aplicar hash y salt
Cómo guardar contraseñas con seguridad: por qué fallan el texto plano, el cifrado y los hashes simples, qué aporta un salt por usuario y por qué la respuesta es un hash lento (Argon2/bcrypt/scrypt). Cómo elegirlo, ajustarlo y migrar un sistema.
"¿Cómo debería guardar exactamente las contraseñas de los usuarios en la base de datos?" — todo desarrollador se topa con esta pregunta una vez. La respuesta es clara. Aquí va la forma segura de guardarlas, por orden, sin pasos de ataque.
Por qué el texto plano, el cifrado y los hashes simples fallan todos
Asume que la base de datos se filtrará algún día. Cuando ocurra, el daño difiere enormemente según el método de almacenamiento.
- Texto plano: toda contraseña queda expuesta en el instante en que se filtra. Peor aún, la reutilización de contraseñas encadena la brecha hacia los otros servicios del usuario. El peor caso.
- Cifrado (reversible): la clave lo revierte — así que si la clave se filtra junto con ello, vuelves al texto plano. Una contraseña nunca necesita leerse de vuelta, así que ser reversible no te aporta nada.
- Hash simple (MD5/SHA-256): un hash rápido permite a un atacante probar conjeturas a gran velocidad. Las contraseñas comunes caen ante las tablas rainbow y la fuerza bruta.
Las "cuatro etapas" hacia lo seguro
Lo más rápido es entenderlo como ir añadiendo una corrección a la vez a un método débil.
La idea clave: un salt y un hash lento hacen trabajos distintos. Un salt derrota la precomputación (tablas rainbow) y el descifrado masivo por reutilización. Un hash lento reduce la fuerza bruta a un ritmo impracticable. Necesitas ambos antes de que sea seguro (→ qué es el hashing).
En la práctica: qué hacer ahora
Usa Argon2id (o bcrypt)
Para sistemas nuevos, haz de Argon2id la primera opción — también puede exigir memoria, lo que resiste la fuerza bruta por GPU. Si prefieres una implementación muy probada, bcrypt es de sobra suficiente en la práctica. Usa la implementación de la biblioteca estándar tal cual para cualquiera de las dos.
Deja que el salt sea automático
bcrypt/Argon2 generan un salt por usuario y lo incrustan dentro de la cadena del hash. No mantienes una columna de salt aparte. Construir a mano MD5(salt + contraseña) es el clásico error que no hay que cometer.
Ajusta el coste para tu entorno
Fija el factor de coste de bcrypt, o la memoria/iteraciones/paralelismo de Argon2, tan alto como puedas mientras un inicio de sesión legítimo siga sintiéndose instantáneo. A medida que los servidores se vuelven más rápidos, también los atacantes — revísalo y súbelo cada año.
Verifica con la función de comparación estándar
Al iniciar sesión, coteja la entrada con el hash almacenado usando la función de verificación de la biblioteca (la mayoría usa una comparación de tiempo constante, segura ante ataques de temporización). No escribas tu propia igualdad de cadenas.
Migra los hashes débiles re-hasheando al iniciar sesión
Si ya guardas MD5/SHA-256, en el momento en que un usuario inicia sesión correctamente, re-hashea con el nuevo esquema y guárdalo. Para los usuarios que no han iniciado sesión, puedes hacer una mejora provisional envolviendo el hash existente dentro de bcrypt (un hash en capas).
Errores comunes vs. la implementación correcta
Error común
- Cifrar las contraseñas para guardarlas (la clave se filtra → se acabó)
- Guardar
MD5(contraseña)oSHA-256(contraseña)directamente - Usar un único salt compartido para todos los usuarios
- Construir a mano
hash(salt + contraseña)
Implementación correcta
- Hash de un solo sentido con Argon2id / bcrypt
- Un salt por usuario (la biblioteca lo añade automáticamente)
- Coste tan alto como resulte aceptable, subido periódicamente
- Usar las funciones estándar de hash/verificación tal cual
La visión de este sitio: el almacenamiento de contraseñas es un sitio para montar sobre la respuesta aburrida y probada
El almacenamiento de contraseñas no es un sitio para ser creativo. Montar sobre las implementaciones estándar de Argon2/bcrypt, probadas por todo el mundo, es lo más seguro y lo más fácil de operar. Nuestra postura: no innoves aquí. Gasta tu esfuerzo, en cambio, en reducir la dependencia de las contraseñas en general — una autenticación multifactor (MFA) exhaustiva, y un eventual paso a passkeys (sin contraseña). Ni siquiera el hash más fuerte puede salvar una contraseña débil y reutilizada.
Sigue leyendo
- Glosario: qué es el hashing de contraseñas / qué es un salt
- Fundamentos: guardar contraseñas de la forma correcta (lado del usuario) / elegir un gestor de contraseñas
- Defensa: elegir el MFA de la forma correcta (reduce la dependencia de las contraseñas)
- Glosario: qué es una passkey (avanzar hacia lo sin contraseña)
FAQ
Q¿No basta con cifrar las contraseñas para guardarlas?
No — el cifrado es la herramienta equivocada. El cifrado es reversible con una clave, así que si la clave se filtra, todas las contraseñas vuelven a texto plano. Una contraseña nunca necesita volver a leerse, ni siquiera por ti, así que un 'hash' irreversible es la respuesta correcta. Pero un hash simple no basta; combina un salt por usuario con un hash lento (bcrypt/Argon2).
Q¿bcrypt o Argon2 — cuál usar?
Para desarrollo nuevo, Argon2 (Argon2id en especial) es la primera opción: te deja ajustar tanto memoria como cómputo, lo que resiste bien la fuerza bruta por GPU. Si valoras una implementación consolidada y muy probada, bcrypt sigue siendo perfectamente práctico. El punto clave con cualquiera de los dos es usar la implementación de la biblioteca estándar tal cual y no inventarla tú.
QYa guardé contraseñas con MD5/SHA-256. ¿Cómo migro?
No puedes reconvertirlas en bloque a texto plano (esa es la idea), así que migra al iniciar sesión. En el momento en que un usuario inicia sesión correctamente, re-hashea la contraseña correcta que acaba de introducir con el nuevo esquema (p. ej. Argon2id) y guárdala. Para los usuarios que no han iniciado sesión, puedes hacer una mejora provisional envolviendo el hash existente dentro de bcrypt (un hash en capas).