bcrypt para hashing de contraseñas Por qué la encriptación sola no es suficiente
Si almacenas contraseñas de usuarios con AES, RSA o incluso SHA-256, estás haciendolo mal. No un poco mal — mal de forma fundamental. Este es el error de seguridad más común en el desarrollo web, y tiene una solución sencilla: utiliza una función adecuada de hashing de contraseñas como bcrypt.
Aquí te explico por qué importa, cómo funciona bcrypt y qué código listo para producción se ve.
Por qué las contraseñas necesitan tratamiento especial
La mayoría de las operaciones criptográficas están diseñadas para ser reversibles o rápidas. La encriptación es reversible por diseño — eso es su propósito completo. SHA-256 y MD5 son rápidas, procesan gigabytes por segundo. Ambas propiedades son catastróficas para contraseñas.
Cuando un atacante obtiene tu base de datos, obtiene tus hashes de contraseñas. Con encriptación, si encuentran la clave, descifran todo. Con hashes rápidos como MD5 o SHA-256, realizan un ataque de fuerza bruta acelerado por GPU — la hardware moderno puede probar cientos de mil millones de hashes de MD5 por segundo. Tu contraseña “compleja” se crackea en minutos.
Las contraseñas necesitan ser:
- No reversibles — incluso con la clave o el algoritmo, no puedes recuperar el texto plano
- Lentos de calcular — la lentitud deliberada hace que los ataques de fuerza bruta sean impracticables
- Únicos por usuario — contraseñas idénticas deben producir hashes diferentes
bcrypt cumple con los tres. Las funciones de hashing generales y la encriptación no.
Hashing vs Encriptación vs Hashing de contraseñas
Estos no son intercambiables:
| Enfoque | Reversible? | Rápido? | Seguro para contraseñas? |
|---|---|---|---|
| Encriptación (AES, RSA) | Sí — con clave | Sí | No |
| Hash rápido (MD5, SHA-256) | No | Sí (por diseño) | No |
| Hashing de contraseñas (bcrypt, Argon2id) | No | No (por diseño) | Sí |
La reversibilidad de la encriptación es un punto de ruptura: si se compromete la clave, se comprometen todas las contraseñas. El rendimiento rápido también es un punto de ruptura: la velocidad permite ataques de fuerza bruta. Las funciones de hashing de contraseñas están diseñadas para ser lentas — y eso es el punto.
Cómo funciona bcrypt
bcrypt, diseñado en 1999 por Niels Provos y David Mazières, realiza tres cosas importantes:
1. Salting. Antes de hashing, bcrypt genera un sal aleatorio (16 bytes) y lo incluye en la salida del hash. Aunque dos usuarios comparten la misma contraseña, sus hashes son diferentes. Esto elimina completamente los ataques con tablas de rayos precalculados.
2. Factor de trabajo (costo). bcrypt acepta un parámetro de costo (normalmente 10–14). Cada incremento duplica el tiempo de cálculo. En costo 12, el hashing tarda aproximadamente 250–400ms en hardware moderno. Eso es lentamente perceptible para una solicitud de inicio de sesión — pero convierte un millón de intentos de fuerza bruta en una operación que dura décadas.
3. La salida es autónoma. Un hash de bcrypt se ve como $2b$12$... y codifica la versión del algoritmo, el factor de costo, el sal y el hash juntos. No necesitas una columna separada de sal. Almacena la cadena completa.
Código de producción: Node.js y Python
Node.js (bcryptjs o bcrypt)
const bcrypt = require('bcrypt');
const SALT_ROUNDS = 12;
// Hash a password
async function hashPassword(plaintext) {
return bcrypt.hash(plaintext, SALT_ROUNDS);
}
// Verify a password against a stored hash
async function verifyPassword(plaintext, storedHash) {
return bcrypt.compare(plaintext, storedHash);
}
// Usage
const hash = await hashPassword('hunter2');
// Store `hash` in your database
const isValid = await verifyPassword('hunter2', hash);
// true
Python (bcrypt)
import bcrypt
COST = 12
def hash_password(plaintext: str) -> bytes:
salt = bcrypt.gensalt(rounds=COST)
return bcrypt.hashpw(plaintext.encode('utf-8'), salt)
def verify_password(plaintext: str, stored_hash: bytes) -> bool:
return bcrypt.checkpw(plaintext.encode('utf-8'), stored_hash)
# Usage
hashed = hash_password('hunter2')
# Store hashed in your database
is_valid = verify_password('hunter2', hashed)
# True
Almacena la cadena completa del hash. Nunca almacenes texto plano, nunca almacenes el sal por separado, nunca almacenes valores intermedios.
Elección del factor de trabajo
El costo adecuado depende de tu hardware. El objetivo: hacer que cada operación de hashing tarde 200–500ms en tu servidor de producción. Eso es lo suficientemente rápido para una buena experiencia de usuario, y lo suficientemente lento para frustrar a los atacantes.
Recomendación actual: costo 12 como mínimo, 14 para cuentas de alto valor (administrador, financiero). Realiza un benchmark en tu hardware real:
// Node.js: benchmark different cost factors
const bcrypt = require('bcrypt');
for (let cost = 10; cost <= 14; cost++) {
const start = Date.now();
await bcrypt.hash('benchmark', cost);
console.log(`Cost ${cost}: ${Date.now() - start}ms`);
}
Si el costo 12 tarda menos de 100ms, aumenta el costo. Si el costo 14 tarda más de 1000ms, reduce a 13. Revisa anualmente — el hardware se vuelve más rápido, y tu factor de costo debe mantenerse actualizado.
Puedes probar el hashing de bcrypt de forma interactiva con el IO Tools generador de hashes de bcrypt.
bcrypt vs Argon2id vs scrypt
bcrypt ha sido probado y ampliamente soportado. Pero tiene una limitación: no es duro en memoria. Un atacante con hardware especializado (ASICs o FPGAs) puede paralelizar los ataques de forma más eficiente que con alternativas que requieran más memoria.
| Algoritmo | Duro en memoria | Resistente a la paralelización | Recomendación |
|---|---|---|---|
| bcrypt | No | Parcial | Buena opción por defecto; usa costo ≥12 |
| scrypt | Sí | Parcial | Mejor que bcrypt, menos herramientas |
| Argon2id | Sí | Sí | Preferido para nuevos proyectos |
Para nuevos proyectos: usa Argon2id. Ganó la Competencia de Hashing de Contraseñas (2015), es duro en memoria, resiste ataques de GPU y ASIC, y ahora está en las recomendaciones de OWASP. La API es casi idéntica a bcrypt.
Para proyectos existentes: si ya estás usando bcrypt con un costo razonable, la migración no es urgente. Agreguélo en tu próxima refactorización principal.
Errores comunes en la implementación
Hashar el hash. Algunos desarrolladores hashan la contraseña en el cliente antes de enviarla, y luego la hashan nuevamente en el servidor. El hash del cliente se convierte en la "contraseña". Ahora estás hashando una cadena de hexa de longitud fija, no una contraseña elegida por el usuario — no pierdes nada con el doble hash, pero tampoco ganas nada, y introduces confusión.
El problema de la truncación de 72 bytes. bcrypt ignora silenciosamente todo lo que excede los 72 bytes. Una contraseña de 100 caracteres y una de 72 caracteres que comparten los primeros 72 bytes son idénticas para bcrypt. Si tus usuarios establecen contraseñas muy largas, esto representa una degradación silenciosa de la seguridad. Mitigación: hash previamente con SHA-256 antes de pasarla a bcrypt — pero solo si comprendes completamente las implicaciones, y documentas claramente.
Factor de trabajo débil. El costo 10 era razonable en 2011. En 2026, usa al menos 12. Si tus registros existentes usan costo 10, puedes actualizarlos de forma transparente: tras un inicio de sesión exitoso, re-hash la contraseña verificada con el nuevo costo y almacena el hash actualizado.
La asincronía importa. bcrypt es intensivo en CPU. Usa siempre la API asincrónica (como se muestra arriba) en Node.js para evitar bloquear el bucle de eventos. El bcrypt sincrónico en un servidor Node hará que cada solicitud esperen.
Migración de MD5/SHA a bcrypt
No puedes re-hash sin el texto plano original. Pero puedes migrar oportunísticamente:
- Agrega una
password_hashcolumna junto a la antiguapassword_md5columna - En un inicio de sesión exitoso (cuando tengas el texto plano), hasha la contraseña con bcrypt y almacénala en
password_hash, elimina la columna antigua - Después de un período de migración, los usuarios que no han iniciado sesión pueden forzarse a restablecer su contraseña
- Una vez
password_md5esté vacía para todos los usuarios, elimina la columna
Este es el enfoque estándar y requiere sin interrupciones.
En pocas palabras
La encriptación se usa para datos que necesitas recuperar. El hashing se usa para datos que necesitas verificar. Las contraseñas deben verificarse, no recuperarse — lo que significa que la encriptación es la herramienta incorrecta.
bcrypt te da salting, lentitud configurable y un formato de hash autónomo. Ha sido la respuesta correcta durante 25 años. Usa un costo de 12 o superior, usa Argon2id para nuevos proyectos, y migra de MD5 y SHA-256 lo antes posible.
Hacer esto mal no es un riesgo hipotético. Las bases de datos se comprometen. Cuando lo hacen, las contraseñas correctamente hashadas con bcrypt son computacionalmente imposibles de crackear. Los hashes de MD5 se crackean en una noche.
Instalar extensiones
Agregue herramientas IO a su navegador favorito para obtener acceso instantáneo y búsquedas más rápidas
恵 ¡El marcador ha llegado!
Marcador es una forma divertida de llevar un registro de tus juegos, todos los datos se almacenan en tu navegador. ¡Próximamente habrá más funciones!
Herramientas clave
Ver todo Los recién llegados
Ver todoActualizar: Nuestro última herramienta se agregó el 27 abr. 2026
