Las funciones hash están en todas partes del desarrollo de software, aunque no te des cuenta. Cada vez que descargas un archivo y verificas su checksum, inicias sesión en un sitio web, haces un commit en Git o minas Bitcoin (bueno, quizás eso último no), las funciones hash hacen el trabajo pesado entre bastidores.
Entendamos qué son, cómo se diferencian y cuándo usar cada una.
¿Qué es una función hash?
Una función hash toma cualquier entrada — un solo carácter, una novela, un archivo de video de 4 GB — y produce una salida de tamaño fijo llamada "hash" o "digest". Piensa en ella como una huella digital para datos. Sin importar cuán grande o pequeña sea la entrada, la salida siempre tiene la misma longitud.
Esto es lo que hace especiales a las funciones hash:
- Determinista: La misma entrada siempre da la misma salida.
hash("hello")devolverá el mismo valor cada vez, en cualquier máquina, en cualquier lenguaje de programación. - Unidireccional: No puedes obtener la entrada a partir de la salida. Dado un hash, no hay forma de descifrar qué lo produjo (aparte de adivinar). Esto es lo que las hace útiles para almacenar contraseñas.
- Efecto avalancha: Cambia un bit de la entrada y la salida cambia drásticamente. Por ejemplo, los hashes SHA-256 de "hello" y "Hello" son cadenas completamente diferentes.
- Resistencia a colisiones: Debería ser prácticamente imposible encontrar dos entradas diferentes que produzcan el mismo hash. Cuanto más fuerte es el algoritmo, más difícil es encontrar colisiones.
Veámoslo en acción:
Salidas totalmente diferentes a partir de entradas que difieren en solo un carácter — una "h" minúscula contra una "H" mayúscula. Eso es el efecto avalancha en acción.
Cómo funcionan las funciones hash por dentro
Aunque las matemáticas detrás de las funciones hash son complejas, el proceso general es directo. La mayoría de las funciones hash siguen estos pasos:
1. Relleno (Padding): El mensaje de entrada se rellena para que su longitud sea un múltiplo de un tamaño de bloque fijo (por ejemplo, 512 bits para SHA-256). Esto asegura que el algoritmo pueda procesar los datos en bloques uniformes.
2. División en bloques: El mensaje rellenado se divide en bloques de tamaño fijo.
3. Rondas de compresión: Cada bloque se procesa a través de múltiples rondas de operaciones a nivel de bits — XOR, AND, OR, desplazamientos de bits y adición modular. Estas operaciones mezclan los bits a fondo para que cada bit de salida dependa de cada bit de entrada.
4. Encadenamiento: La salida del procesamiento de un bloque alimenta el procesamiento del siguiente bloque. Por eso incluso un cambio diminuto al inicio de la entrada se propaga en cascada a través de cada bloque subsiguiente.
5. Digest final: Después de procesar todos los bloques, el estado interno se emite como el valor hash final.
La idea clave es que estas operaciones son fáciles de calcular hacia adelante pero prácticamente imposibles de revertir. No puedes "desmezclar" los bits para recuperar la entrada original.
MD5: El veterano jubilado
MD5 fue diseñado por Ronald Rivest en 1991 y produce un hash de 128 bits (32 caracteres hexadecimales). Fue la opción predeterminada durante décadas — veías checksums MD5 junto a cada descarga de archivos en internet.
Sin embargo, MD5 ahora se considera criptográficamente roto. Los investigadores han demostrado ataques de colisión prácticos — lo que significa que pueden crear dos archivos diferentes que producen el mismo hash MD5. En 2008, investigadores usaron una colisión MD5 para crear un certificado SSL falso, demostrando que esto no era solo una debilidad teórica.
Aún aceptable para: verificaciones de integridad de archivos (confirmar que una descarga se completó correctamente), checksums para deduplicación, tablas hash no relacionadas con seguridad y fingerprinting rápido de datos donde la seguridad no es una preocupación.
Nunca usar para: almacenamiento de contraseñas, firmas digitales, certificados de seguridad, o cualquier cosa donde alguien pueda crear colisiones deliberadamente.
SHA-1: También jubilado
SHA-1 fue diseñado por la NSA y publicado en 1995. Produce un hash de 160 bits (40 caracteres hexadecimales). Durante años fue el estándar en certificados SSL, firmas PGP y sistemas de control de versiones.
Quedó obsoleto después de que Google demostrara un ataque de colisión práctico en 2017 (el famoso ataque SHAttered). Crearon dos archivos PDF diferentes con el mismo hash SHA-1. El ataque requirió 9.223.372.036.854.775.808 cálculos SHA-1 — enorme, pero factible con recursos modernos de computación en la nube.
Git todavía usa SHA-1 internamente para los hashes de commits, pero está migrando a SHA-256. Los navegadores y las autoridades de certificación dejaron de aceptar certificados SHA-1 hace años. Si ves SHA-1 siendo usado para seguridad en cualquier código hoy, debería marcarse y migrarse.
SHA-256 y SHA-512: Los estándares actuales
Estos son parte de la familia SHA-2, diseñada por la NSA y publicada en 2001. Son lo que deberías estar usando hoy para la mayoría de propósitos.
- SHA-256: Salida de 256 bits (64 caracteres hex). Usado en Bitcoin, certificados TLS y la mayoría de aplicaciones de seguridad. Es el equilibrio perfecto entre seguridad y rendimiento. Todo el sistema de prueba de trabajo de Bitcoin está construido sobre doble hashing SHA-256.
- SHA-512: Salida de 512 bits (128 caracteres hex). Mayor salida significa más resistencia a colisiones. Curiosamente, SHA-512 suele ser más rápido que SHA-256 en procesadores de 64 bits porque opera nativamente con palabras de 64 bits, mientras que SHA-256 usa palabras de 32 bits.
- SHA-384 y SHA-512/256: Estas son variantes truncadas de SHA-512. SHA-384 te da una salida de 384 bits, mientras que SHA-512/256 da una salida de 256 bits pero con los beneficios de rendimiento de las operaciones de 64 bits de SHA-512.
Comparación rápida:
SHA-3: La próxima generación
SHA-3 fue estandarizado en 2015 tras una competencia pública organizada por el NIST. A diferencia de SHA-2, que usa una construcción Merkle-Damgard, SHA-3 se basa en la construcción esponja Keccak — un diseño fundamentalmente diferente.
¿Por qué importa esto? Si un avance matemático alguna vez compromete el enfoque de diseño de SHA-2, SHA-3 no se vería afectado porque funciona de manera completamente diferente. Es una póliza de seguro para la comunidad criptográfica.
SHA-3 viene en los mismos tamaños de salida — SHA3-256, SHA3-384, SHA3-512 — y también introduce SHAKE128 y SHAKE256, que son "funciones de salida extensible" que pueden producir un hash de cualquier longitud deseada.
En la práctica, SHA-2 sigue siendo más ampliamente usado y más rápido en la mayoría del hardware. La adopción de SHA-3 está creciendo, pero es más un estándar de respaldo que un reemplazo.
Casos de uso en el mundo real
Control de versiones Git: Cada commit, árbol y blob en Git se identifica por su hash SHA-1. Cuando ejecutas git commit, Git hashea el contenido de tus cambios, la estructura del árbol, el hash del commit padre, tu información de autor y la marca de tiempo. Por eso los hashes de commits se ven como a1b2c3d4e5f6... — son literalmente digests SHA-1.
Minería de Bitcoin: Los mineros compiten para encontrar un valor nonce que, combinado con los datos del bloque y hasheado con doble SHA-256, produzca un hash por debajo de un umbral objetivo. La dificultad de encontrar este hash es lo que asegura toda la red. Para 2024, la red Bitcoin calcula aproximadamente 500 trillones de hashes SHA-256 por segundo.
Deduplicación de archivos: Servicios de almacenamiento en la nube como Dropbox hashean cada archivo que subes. Si el hash coincide con un archivo existente, no almacenan un duplicado — simplemente añaden un puntero. Esto ahorra enormes cantidades de almacenamiento.
Firmas digitales: Cuando firmas un documento o un lanzamiento de software, no estás firmando el archivo completo. En su lugar, el archivo se hashea y el hash es lo que se firma con tu clave privada. El destinatario hashea el archivo por su cuenta y verifica la firma contra ese hash.
Autenticación de APIs: HMAC (Código de Autenticación de Mensajes basado en Hash) combina una clave secreta con un hash del mensaje para verificar tanto la integridad como la autenticidad de las solicitudes API. AWS, Stripe y la mayoría de las APIs principales usan HMAC-SHA256 para la firma de solicitudes.
Errores comunes que cometen los desarrolladores con el hashing
Usar funciones hash para contraseñas: SHA-256 simple es demasiado rápido para hashear contraseñas. Un atacante con una GPU puede calcular miles de millones de hashes SHA-256 por segundo, haciendo triviales los ataques de fuerza bruta. Siempre usa funciones de hashing de contraseñas diseñadas para ese propósito como bcrypt, scrypt o Argon2, que son intencionalmente lentas y consumen mucha memoria.
No usar salt: Si hasheas contraseñas sin un salt (un valor aleatorio añadido a cada contraseña antes del hashing), contraseñas idénticas producen hashes idénticos. Un atacante con una "rainbow table" precalculada puede buscar contraseñas comunes al instante. Siempre añade un salt único y aleatorio por usuario.
Comparar hashes de manera insegura en tiempo: Usar == para comparar hashes en código sensible a la seguridad puede filtrar información a través de canales laterales de tiempo. Un atacante puede medir cuánto tarda la comparación y deducir el hash carácter por carácter. Usa funciones de comparación en tiempo constante como crypto.timingSafeEqual() en Node.js o hmac.compare_digest() en Python.
Truncar hashes: Algunos desarrolladores truncan hashes para ahorrar espacio (por ejemplo, almacenar solo los primeros 16 caracteres de un hash SHA-256). Esto reduce drásticamente la resistencia a colisiones. Un hash SHA-256 completo tiene 2^256 valores posibles; truncar a 16 caracteres hexadecimales deja solo 2^64 — un número que el hardware moderno puede forzar por fuerza bruta.
¿Cuál función hash usar?
- Integridad de archivos (sin seguridad): SHA-256 o incluso MD5 está bien. Estás verificando corrupción accidental, no manipulación maliciosa.
- Almacenamiento de contraseñas: ¡Ninguna de estas! Usa bcrypt, scrypt o Argon2 — son deliberadamente lentas, lo que hace los ataques de fuerza bruta impracticables. Las funciones hash regulares son demasiado rápidas para hashear contraseñas.
- Firmas digitales y certificados: SHA-256 o SHA-512.
- HMAC (autenticación de mensajes): SHA-256 o SHA-512.
- Direccionamiento de contenido estilo Git: SHA-256 (hacia donde se dirige Git).
- Preparación para el futuro: Si estás construyendo un sistema que necesita durar décadas y quieres un plan de respaldo en caso de que SHA-2 sea comprometido, considera SHA-3.
- Checksums en pipelines de datos: SHA-256 para verificación de integridad de datos entre etapas del pipeline. CRC32 es más rápido pero solo detecta errores accidentales, no manipulación intencional.
Funciones hash en código: Ejemplos prácticos
Bueno, suficiente teoría — escribamos algo de código. Porque honestamente, la mejor manera de entender el hashing es simplemente... hacerlo. Así es como calculas hashes en los lenguajes que probablemente usas todos los días.
Node.js — El módulo crypto incorporado lo hace muy sencillo:
Y la parte genial — hashear un archivo es casi lo mismo:
Python — El hashlib de Python es igual de directo. De hecho, creo que Python tiene la API más elegante para esto:
Go — La biblioteca estándar de Go está increíblemente bien diseñada para esto:
Java — Un poco más verboso (porque... Java), pero funciona perfectamente:
Verificar una descarga de archivo: Este es uno de los usos más prácticos del hashing. Digamos que descargas un ISO de Linux y el sitio web dice que el checksum SHA-256 debería ser abc123.... Así lo verificas:
Sé que esto parece básico, pero te sorprendería cuántos desarrolladores se saltan este paso. Un solo byte corrupto en una descarga de 4 GB puede arruinarte toda la tarde.
Rainbow Tables y por qué son aterradoras
Okay, esta es la parte que me voló la cabeza cuando la aprendí por primera vez. Imagina que alguien precalcula el hash de cada contraseña posible hasta, digamos, 8 caracteres. Cada combinación de letras, números y símbolos. Almacenan todas esas correspondencias hash-a-contraseña en una tabla de búsqueda gigante.
Eso es una rainbow table. Y son absolutamente aterradoras.
La razón: si almacenaste contraseñas como hashes SHA-256 simples (sin salt), un atacante que obtiene tu base de datos no necesita "crackear" nada. Simplemente busca cada hash en su rainbow table. ¡Bum! — recuperación instantánea de contraseñas. La búsqueda toma microsegundos.
¿Qué tan grandes son estas tablas? Una rainbow table que cubre todas las contraseñas alfanuméricas de hasta 8 caracteres puede ocupar unos 100-200 GB. Suena como mucho, pero eso cabe en un solo SSD. Sitios como CrackStation tienen tablas con miles de millones de hashes precalculados, y crackean hashes de contraseñas comunes en segundos de forma gratuita.
Ahora las buenas noticias: el salting derrota completamente a las rainbow tables. Un salt es simplemente una cadena aleatoria que añades a la contraseña antes de hashear:
¿Ves lo que pasó? La misma contraseña ("password123") produce hashes completamente diferentes por los diferentes salts. Un atacante necesitaría construir una rainbow table separada para cada salt posible, lo cual es computacionalmente imposible.
Toda biblioteca moderna de hashing de contraseñas (bcrypt, Argon2, scrypt) maneja el salting automáticamente. Si alguna vez estás tentado a implementar tu propio hashing de contraseñas — no lo hagas. En serio. Usa bcrypt y sigue con tu vida.
HMAC: Hashing con un secreto
HMAC significa Código de Autenticación de Mensajes basado en Hash, y sí, lo sé, suena intimidante. Pero quédate conmigo — en realidad es un concepto bastante simple que probablemente has usado sin darte cuenta.
El hashing regular toma un mensaje y produce un hash. HMAC toma un mensaje Y una clave secreta, y produce un hash. La diferencia clave (juego de palabras intencionado) es que solo alguien que conoce la clave secreta puede producir o verificar el HMAC. Prueba dos cosas a la vez: el mensaje no ha sido manipulado, Y proviene de alguien que conoce el secreto.
¿Dónde ves esto en el mundo real? Firmas de webhooks. Cuando GitHub o Stripe envía un webhook a tu servidor, incluyen una firma HMAC-SHA256 en los headers. Tu servidor puede verificar que el webhook realmente proviene de GitHub (y no fue falsificado por algún atacante aleatorio) calculando el HMAC tú mismo y comparando.
Aquí tienes un ejemplo práctico de verificación de una firma de webhook de GitHub en Node.js:
¿Notas la llamada a timingSafeEqual? Es crucial. Una comparación regular con === retorna false tan pronto como encuentra el primer carácter que no coincide, lo que significa que un atacante puede medir el tiempo de respuesta y descifrar la firma byte por byte. La comparación segura en tiempo siempre toma la misma cantidad de tiempo sin importar dónde ocurra la discrepancia.
Benchmarks de rendimiento de funciones hash
Mira, lo entiendo — el rendimiento importa. Especialmente si estás hasheando millones de archivos en un pipeline de build o procesando un flujo masivo de datos. Así es como se comparan las principales funciones hash en velocidad (benchmarks aproximados en hardware x86_64 moderno):
Espera, ¿captaste eso? BLAKE3 es 10 veces más rápido que SHA-256 siendo criptográficamente seguro. No es un error tipográfico.
BLAKE3 es lo más nuevo y mejor del mundo del hashing, y con buena razón. Está basado en la familia BLAKE2 (que ya superó a SHA-3 en la competencia del NIST) pero rediseñado para aprovechar el paralelismo SIMD y el multithreading. Puede hashear datos básicamente a la velocidad de memcpy.
¿Por qué debería importarte? A las herramientas de build les importa. Mucho. Herramientas como Bazel, Buck y varios sistemas de almacenamiento direccionado por contenido pasan una cantidad sorprendente de tiempo hasheando archivos. Cambiar de SHA-256 a BLAKE3 puede acelerar la verificación de dependencias en un orden de magnitud. El ecosistema de Rust ha estado adoptando BLAKE3 agresivamente, y está apareciendo en más y más lugares.
Dicho esto, SHA-256 y SHA-512 siguen siendo la elección correcta cuando necesitas amplia compatibilidad o cumplimiento con estándares como FIPS. No todo soporta BLAKE3 todavía, y en muchos casos de uso, la velocidad de hashing no es el cuello de botella de todas formas.
Blockchain y árboles de Merkle: Hashing a escala
Okay, aquí es donde se pone realmente interesante. ¿Sabes cómo Git puede decirte exactamente qué archivo cambió en un repositorio masivo? ¿Y cómo Bitcoin puede verificar una transacción sin descargar toda la blockchain? El secreto es una estructura de datos llamada árbol de Merkle (nombrado así por Ralph Merkle, quien lo patentó en 1979).
Un árbol de Merkle es básicamente un árbol de hashes. Así funciona — imagina que tienes cuatro bloques de datos:
Cada nodo hoja es el hash de un bloque de datos. Cada nodo padre es el hash de sus dos hijos concatenados. El hash raíz (a veces llamado "Merkle root") es un solo hash que representa TODOS los datos del árbol.
Aquí está la parte genuinamente elegante: si incluso un bit de Data C cambia, Hash(C) cambia, lo que significa que Hash(CD) cambia, lo que significa que el Root Hash cambia. Puedes detectar manipulación instantáneamente con solo verificar la raíz.
Pero se pone mejor. Digamos que quieres probar que Data C es parte del árbol sin revelar Data A, B o D. Solo necesitas proporcionar: Data C, Hash(D) y Hash(AB). El verificador puede reconstruir el camino hasta la raíz y comprobar que coincide. Esto se llama "prueba de Merkle", y es increíblemente eficiente — para un árbol con un millón de hojas, la prueba tiene solo unos 20 hashes de largo (log2 de 1,000,000).
¿Dónde se usa esto en la práctica?
- Git: Tu repositorio entero es un árbol de Merkle. Los commits apuntan a árboles, los árboles apuntan a blobs, y todo se identifica por su hash SHA-1. Por eso Git puede saber instantáneamente si algo cambió.
- Bitcoin: Cada bloque contiene un Merkle root de todas las transacciones. Los clientes ligeros (como wallets móviles) pueden verificar una transacción específica usando una prueba de Merkle sin descargar el bloque completo.
- IPFS: El Sistema de Archivos InterPlanetario divide archivos en chunks, construye un Merkle DAG (grafo acíclico dirigido) y usa el hash raíz como identificador de contenido (CID) del archivo.
- Certificate Transparency: Los logs de Certificate Transparency de Google usan árboles de Merkle para que cualquiera pueda verificar eficientemente si un certificado fue (o no) registrado.
El futuro: Funciones hash post-cuánticas
Quizás has escuchado que las computadoras cuánticas van a romper toda nuestra encriptación. Y sí, eso es parcialmente cierto — RSA, ECC y Diffie-Hellman están todos acabados una vez que lleguen las computadoras cuánticas a gran escala. El algoritmo de Shor puede factorizar números grandes y calcular logaritmos discretos eficientemente, que es de lo que dependen esos sistemas.
Pero aquí están las buenas noticias sorprendentes: las funciones hash son en realidad bastante seguras contra computadoras cuánticas. La principal amenaza cuántica para las funciones hash es el algoritmo de Grover, que puede buscar en un espacio no estructurado cuadráticamente más rápido. En la práctica, esto significa que se reducen a la mitad los bits de seguridad — SHA-256 pasa de 2^256 a 2^128 de fortaleza contra ataques cuánticos.
2^128 sigue siendo absolutamente enorme. Eso es aproximadamente el número de átomos en el universo observable al cuadrado. Nadie va a romper eso por fuerza bruta, computadora cuántica o no.
Así que mientras NIST está trabajando activamente en estándares de criptografía post-cuántica (y finalizó varios en 2024), la urgencia está principalmente en la encriptación de clave pública y las firmas — no en las funciones hash. Si estás usando SHA-256 hoy, puedes dormir tranquilo sabiendo que las computadoras cuánticas no lo harán inútil.
Dicho esto, si eres verdaderamente paranoico (y en criptografía, la paranoia es una virtud), subir a SHA-512 o SHA3-256 te da un margen de seguridad extra. Algunos esquemas de firma post-cuánticos como SPHINCS+ están construidos completamente sobre funciones hash, lo cual es un bonito voto de confianza en su resistencia cuántica.
Colisiones de hash: Ataques de cumpleaños explicados
Hablemos de una de las cosas más contraintuitivas en toda la ciencia de la computación: el ataque de cumpleaños. Se llama así por la paradoja del cumpleaños, y es la razón por la que las funciones hash necesitan ser más grandes de lo que intuitivamente esperarías.
La paradoja del cumpleaños: en una sala con solo 23 personas, hay un 50% de probabilidad de que dos de ellas compartan cumpleaños. No un cumpleaños específico — simplemente cualquier par que coincida. Con 70 personas, la probabilidad salta a 99.9%. La mayoría adivinaría que necesitas unas 183 personas (la mitad de 365), pero el número real es mucho menor porque buscamos CUALQUIER colisión, no una específica.
Exactamente la misma matemática aplica a las funciones hash. Si una función hash produce N salidas posibles, no necesitas calcular N hashes para encontrar una colisión — solo necesitas aproximadamente la raíz cuadrada de N.
Para un hash de 256 bits como SHA-256, hay 2^256 salidas posibles. Encontrar una colisión requiere aproximadamente 2^128 operaciones (la raíz cuadrada de 2^256). Eso sigue siendo un número imposiblemente grande — pero es la razón por la que no podemos simplemente usar un hash de 64 bits y ya.
Exactamente por esto MD5 (128 bits) colapsó. Su resistencia a colisiones era solo 2^64 desde el principio, y las debilidades estructurales en el algoritmo la redujeron aún más. Los investigadores eventualmente encontraron colisiones en segundos en un laptop normal.
La conclusión práctica: siempre usa al menos una función hash de 256 bits para cualquier cosa relacionada con seguridad. SHA-256, SHA3-256 o BLAKE3 son todas excelentes opciones. Y si alguien sugiere usar un hash de 64 o 128 bits para propósitos de seguridad, ahora sabes exactamente por qué es una idea terrible.
Pruébalo tú mismo
¿Curioso por saber qué hash producen tus datos? Usa nuestro Generador de hash MD5, Generador de hash SHA-256 o Generador de hash SHA-512. Pega algo de texto y observa cómo incluso cambios mínimos producen hashes completamente diferentes — es la mejor manera de desarrollar intuición sobre cómo se comportan estos algoritmos.