¿Odias los anuncios? Ir Sin publicidad Hoy

bcrypt para hashing de contraseñas Por qué la encriptación sola no es suficiente

Publicado el
bcrypt para hashing de contraseñas: por qué la encriptación sola no es suficiente 1
ANUNCIO · ¿ELIMINAR?

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:

  1. No reversibles — incluso con la clave o el algoritmo, no puedes recuperar el texto plano
  2. Lentos de calcular — la lentitud deliberada hace que los ataques de fuerza bruta sean impracticables
  3. Ú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:

EnfoqueReversible?Rápido?Seguro para contraseñas?
Encriptación (AES, RSA)Sí — con claveNo
Hash rápido (MD5, SHA-256)NoSí (por diseño)No
Hashing de contraseñas (bcrypt, Argon2id)NoNo (por diseño)

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.

AlgoritmoDuro en memoriaResistente a la paralelizaciónRecomendación
bcryptNoParcialBuena opción por defecto; usa costo ≥12
scryptParcialMejor que bcrypt, menos herramientas
Argon2idPreferido 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:

  1. Agrega una password_hash columna junto a la antigua password_md5 columna
  2. 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
  3. Después de un período de migración, los usuarios que no han iniciado sesión pueden forzarse a restablecer su contraseña
  4. Una vez password_md5 esté 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.

¿Quieres eliminar publicidad? Adiós publicidad hoy

Instalar extensiones

Agregue herramientas IO a su navegador favorito para obtener acceso instantáneo y búsquedas más rápidas

añadir Extensión de Chrome añadir Extensión de borde añadir Extensión de Firefox añadir Extensión de Opera

¡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!

ANUNCIO · ¿ELIMINAR?
ANUNCIO · ¿ELIMINAR?
ANUNCIO · ¿ELIMINAR?

Noticias Aspectos técnicos clave

Involucrarse

Ayúdanos a seguir brindando valiosas herramientas gratuitas

Invítame a un café
ANUNCIO · ¿ELIMINAR?