Skip to content
Comparativa de funciones hash — MD5, familia SHA, bcrypt, Argon2

Hashing y cripto

Comparativa de funciones hash — MD5, familia SHA, bcrypt, Argon2

No todas las funciones hash hacen el mismo trabajo. Cuándo MD5 sigue siendo válido, por qué SHA-256 es el default, y por qué nunca debes hashear passwords con ninguno.

“Función hash” cubre tres categorías de trabajo que solo se tocan en los bordes: digests criptográficos, funciones de password-hashing, y checksums rápidos no criptográficos. Usar la categoría equivocada para el trabajo es como se produjeron la mayoría de fugas de passwords del sector. Esta guía es la separación práctica.

Las tres categorías

CategoríaObjetivoEjemplosVelocidad¿Vale para passwords?
Digest criptográficoResistencia a colisión y preimagenSHA-256, SHA-3, BLAKE3RápidoNo
Password-hashingLento a propósito, memory-hardbcrypt, Argon2id, scryptLento (parametrizable)Sí — es el trabajo
Checksum no criptográficoDetectar corrupción accidentalCRC32, xxHash, MurmurHashMuy rápidoNo

Error común: tirar de SHA-256 para “hashear el password”. SHA-256 es digest criptográfico y es extremadamente rápido. Esa velocidad es el problema: una sola GPU calcula miles de millones de SHA-256 por segundo contra una base de datos de passwords robada. Las funciones de password-hashing (bcrypt, Argon2id) están diseñadas para ser lentas y caras de forma ajustable.

Los digests criptográficos

AlgoritmoSalidaEstadoCuándo usar
MD5128 bitsRoto para seguridadSolo checksums; deduplicación de archivos, claves de caché
SHA-1160 bitsRoto (SHAttered 2017)Solo legacy; Git lo usa pero migra a SHA-256
SHA-256256 bitsEstándar actualElección por defecto para cualquier digest criptográfico
SHA-512512 bitsEstándar actualMisma clase de seguridad; a veces más rápido en CPUs 64-bit
SHA-3 (Keccak)224-512 bitsEstándar actualAlternativa aprobada por NIST con diseño distinto; úsalo si necesitas primitivas diversas
BLAKE3AjustableModerno, rápidoDigest paralelo muy rápido; más nuevo, menos batalla

MD5 no es palabrota. Está roto para uso criptográfico — se pueden construir colisiones barato — pero va bien para detección de corrupción accidental. Si checksumeas un archivo contra corrupción en tránsito, MD5 o incluso CRC32 es más rápido que SHA-256 y el modelo de ataque no importa. Si verificas que un archivo no ha sido manipulado por un atacante, necesitas SHA-256 o superior.

# Estos comandos hashean el mismo archivo con algoritmos distintos
md5sum file.tar
sha256sum file.tar
b3sum file.tar      # BLAKE3, si está instalado

SHA-1 sobrevivió algo más que MD5 porque su coste de colisión era mayor, pero Google demostró una colisión práctica en 2017 (el paper SHAttered). Git sigue usando SHA-1 para IDs de objeto pero tiene ruta de migración a SHA-256. Los sistemas nuevos deben defaultear a SHA-256.

Funciones de password-hashing

El modelo de ataque para passwords es offline: un atacante roba tu BD y la crackea en su propio hardware. Tu defensa es hacer que cada computación de hash sea lo bastante cara como para que una granja de GPUs no pueda reventar los passwords débiles antes de que puedas responder.

Dos cosas que necesitas:

  • Una función lenta, parametrizada para poder escalarla con el tiempo mientras el hardware mejora.
  • Un salt único por usuario, guardado con el hash, para que tablas rainbow precomputadas sean inservibles.

Las opciones recomendadas actuales, en orden aproximado de modernidad:

  1. Argon2id — Recomendación OWASP 2026. Memory-hard (los atacantes no pueden tirar solo GPUs), resistente a canales laterales, ganó la Password Hashing Competition en 2015. Úsalo si tu stack tiene librería auditada (argon2-cffi en Python, argon2 en Node, golang.org/x/crypto/argon2 en Go).
  2. bcrypt — El estándar de 1999, sigue bien. Bien auditado, ubicuo. El parámetro de coste (el “work factor”) es ajustable. Las librerías tienen límites de longitud de password (72 bytes para bcrypt) que debes manejar — o truncar o pre-hashear con SHA-256 antes de bcrypt.
  3. scrypt — Memory-hard como Argon2. Menos usado hoy porque Argon2 subsume sus objetivos de diseño, pero sigue seguro.
  4. PBKDF2 — Aprobado por FIPS. Úsalo solo si tienes razón de compliance; si no, es estrictamente peor que Argon2id.
# Argon2id en Python (argon2-cffi)
from argon2 import PasswordHasher

ph = PasswordHasher(
    time_cost=3,       # iteraciones
    memory_cost=64_000, # 64 MiB
    parallelism=4,
)
hash_ = ph.hash("user-password")
# "$argon2id$v=19$m=65536,t=3,p=4$saltsalt...$hash..."

ph.verify(hash_, "user-password")  # True

Regla: nunca uses SHA-256, SHA-3, BLAKE3, o cualquier digest criptográfico plano para hashear passwords. Ni siquiera con salt. Ni siquiera con múltiples iteraciones (estás reimplementando PBKDF2 mal). Usa una función de password-hashing.

Para los tokens de auth construidos sobre estos cimientos, mira la pieza JWT en 2026; para el encoding URL-safe de tokens que los suele acompañar, la guía de Base64 en producción.

Hashes no criptográficos

Estas son herramientas para hash tables, claves de caché, almacenamiento content-addressed, y Bloom filters. Son rápidas y distribuyen bien la entrada, pero se colisionan trivialmente por diseño.

AlgoritmoVelocidadCuándo usar
CRC32Muy rápido, 32 bitsChecksums de archivos en formatos legacy, integridad rápida
FNVRápido, cualquier anchoHashing de strings en apps simples
MurmurHash3Rápido, bien distribuidoHash tables, Bloom filters, sharding de caché
xxHashMás rápido no-cryptoChecksums de payloads grandes, dedup de logs
CityHash / FarmHashRápidoOrigen Google; Protocol Buffers y BigQuery usan parientes

Nunca uses estos contra input no confiable cuando las colisiones importen para seguridad. Un atacante puede fabricar inputs que colisionen en MurmurHash y usarlo para crear ataques HashDoS contra hash tables — razón por la que los runtimes modernos usan semillas de hash aleatorias en startup.

HMAC — lo que en realidad necesita la mayoría

Si estás firmando un API request, un webhook, o una cookie, no quieres un hash crudo. Quieres HMAC (Hash-based Message Authentication Code). HMAC envuelve un hash criptográfico con una clave de forma que demuestra que el mensaje fue firmado por alguien con la clave.

import hmac, hashlib

key = b"secret"
message = b"payload=value&ts=123"
signature = hmac.new(key, message, hashlib.sha256).hexdigest()

Tres cosas que HMAC protege y un sha256(key + message) crudo no:

  1. Ataques de extensión de longitud contra la concatenación cruda.
  2. Ataques de truncamiento.
  3. Mal uso del estado interno del hash.

Default a HMAC-SHA256 para firmar. Stripe, GitHub, la mayoría de providers de webhooks lo usan porque es la opción sin trampas.

Conclusiones

Tres categorías, tres herramientas: un digest criptográfico (SHA-256 por defecto) para integridad; una función de password-hashing (Argon2id o bcrypt) para passwords; un hash no criptográfico (xxHash, Murmur) para hash tables y checksums. MD5 solo está muerto para crypto — sirve para dedup. Usa siempre HMAC cuando firmes, no hash crudo. Nunca hashees passwords con nada que no sea una función dedicada de password-hashing.

Por ·