👉 Si no lo has leído ya, y no sabes mucho acerca de cómo implementar un login, lee esto primero: Por qué un login lento puede evitarte un marrón legendario… o cómo implementar bien un login.
Guardar contraseñas en tu web sin hash es como guardar billetes en una caja de zapatos. Pero incluso si usas hashes, hay trucos de chef que todo buen dev debería aplicar: sal y pimienta.
Sí, como en la cocina.
Y no, no es una metáfora rebuscada. Vamos allá 👇
Supongamos que tenemos MD5 como algoritmo de hashing (cosa que NO deberías hacer según te expliqué en este maravillo post 👉 Por qué un login lento puede evitarte un marrón legendario… o cómo implementar bien un login.
En tu base de datos, tendrás una tabla de contraseñas hasheadas. La columna de la contraseña original no debería existir jamás, pero aquí la incluimos como referencia para que se entienda el ejemplo.
user_id | password (no se guarda) | hash (MD5 del password) |
1 | 123456 | e10adc3949ba59abbe56e057f20f883e |
2 | password123 | 482c811da5d5b4bc6d497ffa98491e38 |
3 | 123456 | e10adc3949ba59abbe56e057f20f883e |
Este método tiene dos problemas gordos:
- Los usuarios que usan la misma contraseña terminan teniendo el mismo hash.
Resultado: si un atacante descubre el password del usuario 1, también conoce el del usuario 3. Fiesta 🎉. - Como estás generando el MD5 directamente desde la contraseña, puedes ir a cualquier web de búsqueda inversa (como md5decrypt.net) y descifrar el hash en segundos.
Literalmente estás regalando las llaves.
Y para solucionar ambas cosas… usamos sal y pimienta.
🧂 ¿Qué es la sal (salt) en criptografía?
Una sal es un valor aleatorio que se añade antes de calcular el hash de una contraseña. Es como si antes de cocinar echaras un ingrediente secreto para que el plato nunca sepa igual, aunque uses los mismos ingredientes.
En este caso, la salt es un valor distinto para cada usuario, y al calcular el hash lo hacemos así:
HASH = MD5(password + salt)
👉 Importante: la salt hay que guardarla en la base de datos, porque sin ella no podríamos verificar el login. Así que ahora la tabla tendrá una columna extra:
user_id | password (no se guarda) | salt | hash (MD5 de pass + salt) |
1 | 123456 | oRmjVGj15 | 40d04a9e37a3aa567ad2ea40072a4641 |
2 | password123 | ORrgKHDjF | e9abff604fc0ad2bcdf3308840329686 |
3 | 123456 | oLDpQn2dQ | d96fcafb2c6d838b6ab4ce1831d39305 |
Ahora los dos problemas anteriores desaparecen:
💡 Aunque dos usuarios tengan la misma contraseña, los hashes son completamente distintos, gracias a que cada uno tiene su propia salt.
Así, aunque un atacante se lleve la base de datos entera, no puede hacer un simple “buscar y comparar” con un diccionario de hashes conocidos (rainbow tables).
De hecho, necesitaría una tabla distinta para cada salt… o sea, infinitas tablas. Y eso no escala ni con IA de moda ni con GPUs de gamer 💸.
🤔 Pero espera… si me roban la base de datos, ¿no tienen también la salt?
Pues sí. Y si además saben qué algoritmo usas (spoiler: lo van a saber), pueden intentar romper un hash concreto así:
- Cogen un diccionario de contraseñas típicas (
123456
,password
,qwerty
, etc). - Las van probando una a una, concatenadas con la salt correspondiente.
- Calculan el hash y lo comparan con el de la tabla.
- Si coincide… bingo 🎯.
Pero aquí viene lo bueno:
- Tienen que hacer esto usuario por usuario. Ya no vale romper un hash y reventar mil cuentas de golpe.
- No pueden usar rainbow tables.
- Y si además estás usando un algoritmo lento como
bcrypt
oArgon2
, el proceso de prueba y error se vuelve muuuy lento. Tan lento que al hacker igual le compensa más ponerse a minar Dogecoin que reventar passwords 🐶💰.
👉 Conclusión (de momento)
Sí, todavía se puede atacar. Pero ya no es trivial, y le estás poniendo muchas piedras en el camino al malo de la peli.
Y si aún quieres más seguridad… 🌶️ Toca echarle pimienta.
🌶️ Añadamos la pimienta (pepper)
La pimienta (pepper) es otro ingrediente secreto, pero con una diferencia importante: no se guarda junto al hash. O mejor dicho, no se guarda en base de datos.
Se trata de una clave secreta, fija para toda la aplicación, que se añade a la contraseña además del salt.
Ahora calculamos nuestro hash así:
HASH = MD5(password + salt + pepper)
Y supongamos la pepper es:
pepper = UCFl1PVFpz53
Ahora nuestra tabla de hashes quedaría así:
user_id | password (no se guarda) | salt | hash (MD5 de pass + salt) |
1 | 123456 | oRmjVGj15 | 919e36790a0b48a67d7e8c10dbbeb4fa |
2 | password123 | ORrgKHDjF | 78cebca31441aa00ff026a78d22d0b09 |
3 | 123456 | oLDpQn2dQ | 99c09fe497f7daae0351db7ebc0f8a35 |
Ahora, aunque un atacante te robe la base de datos, no podrá hacer NADA con ella si no tiene el valor de pepper. Ni siquiera a prueba y error.
Vale, pero y si no guardo el valor de pepper en la base de datos, ¿dónde lo guardo?
Pues vas a flipar porque al igual que decíamos que un login lento tiene sus ventajas, el pepper deberías ponerlo en un archivo de configuración de entorno… si, en un archivo… como si estuviese hardcodeado…
Pero por el amor de todos los chefs digitales: no dejes el bote de pimienta encima de la mesa. O sea, no metas la pepper en el código y lo subas a GitHub como si nada. No seas esa persona. Protégelo aunque sea un poquito.
🧠 En resumen, checklist para no hacer el ridículo
Si vas a guardar contraseñas, hazlo con estilo:
- ✅ Usa un algoritmo resistente como bcrypt, scrypt o Argon2
- ✅ Genera una salt aleatoria y única por usuario
- ✅ Añade pepper (y guárdala fuera de la base de datos)
- ✅ Y nunca, NUNCA uses MD5 o SHA1. Ni aunque venga firmada por tu jefe.
Y léete este post para más consejos buenos buenos 👉 Por qué un login lento puede evitarte un marrón legendario… o cómo implementar bien un login.
🤔 ¿Y si no quiero complicarme?
Pues harás muy bien, porque todo esto ya está inventado.
Usa una librería que lo haga por ti. Casi cualquier framework serio ya incorpora todo esto. Pero al menos ahora sabrás por qué lo hace.
¿Te ha molado el salseo de seguridad? Pues compártelo con ese colega que aún guarda contraseñas en texto plano, a ver si se le indigestan.
Hey! Qué opinas sobre el artículo?