Hashing y firmas digitales
Funciones hash, HMAC, cómo guardar contraseñas con Argon2 y cómo una firma digital prueba integridad, autenticidad y no repudio.
Cifrar protege la confidencialidad, pero muchas veces lo que necesitas no es ocultar un dato, sino demostrar que no ha cambiado y que viene de quien dice. Esa es la integridad, y se construye con una pieza que no es cifrado: la función hashFunción hash criptográficaFunción que reduce una entrada de cualquier tamaño a un valor fijo, irreversible y único en la práctica; cambia por completo ante el mínimo cambio.. Sobre ella se levantan los checksums, HMAC, el almacenamiento de contraseñas y las firmas digitalesFirma digitalHash de un mensaje cifrado con la clave privada del emisor; prueba integridad, autenticidad y no repudio.. Un error muy común es confundir todo esto con “cifrar”; separarlo bien te ahorra fallos de diseño graves.
Qué es (y qué no es) una función hash#
Una función hash criptográfica reduce una entrada de cualquier tamaño a una salida fija —256 bits en SHA-256— con tres propiedades:
- Resistencia a preimagen: dada la salida, no puedes reconstruir la entrada.
- Resistencia a segunda preimagen: dado un mensaje, no puedes encontrar otro con el mismo hash.
- Resistencia a colisiones: no puedes encontrar dos mensajes cualesquiera con el mismo hash.
Además tiene efecto avalancha: cambiar un solo bit de la entrada cambia la mitad de los bits de la salida. Por eso un hash sirve como “huella” de un fichero.
Por qué MD5 y SHA-1 están rotos#
Un hash se considera roto cuando alguien sabe fabricar colisiones. MD5 cae en segundos en un portátil. SHA-1 aguantó más, pero en 2017 el ataque SHAttered (CWI Ámsterdam + Google) produjo la primera colisión real —dos PDF distintos con el mismo SHA-1— y en 2020 SHA-1 is a Shambles demostró una colisión de prefijo elegido, mucho más peligrosa porque permite falsificar certificados y firmas. NIST lleva años retirando SHA-1: su plan es transicionar fuera de SHA-1 en todas las aplicaciones para finales de 2030, y ya está prohibido para generar firmas digitales (SP 800-131A). Usa SHA-256 (o SHA-3) y trata MD5/SHA-1 solo como checksums no de seguridad, nunca para firmas ni contraseñas.
HMAC: integridad con una clave compartida#
Un hash a secas prueba que un fichero no cambió si confías en el hash. Pero si un atacante puede cambiar el dato, también puede recalcular el hash. HMAC (Hash-based MAC) resuelve esto mezclando el mensaje con una clave secreta compartida: HMAC(clave, mensaje). Solo quien tiene la clave puede generar o verificar el código, así que prueba integridad + autenticidad entre dos partes que comparten un secreto. Es lo que valida un webhook firmado, una cookie de sesión o una petición firmada de la API de AWS (SigV4 usa HMAC-SHA-256).
Contraseñas: salting y KDFs lentas#
Las contraseñas no se cifran y no se guardan con un hash normal. Un hash rápido como SHA-256 es justo lo que no quieres: un atacante con la base de datos robada prueba miles de millones de candidatos por segundo. Dos ideas lo arreglan:
- Sal (salt): un valor aleatorio único por usuario que se concatena antes de hashear. Impide las rainbow tables y que dos usuarios con la misma contraseña tengan el mismo hash.
- KDF lenta y con memoria: una función diseñada para ser cara de calcular, con un coste ajustable. OWASP recomienda Argon2id como primera opción (ganó la Password Hashing Competition de 2015), y si no está disponible, en este orden: scrypt, bcrypt (solo sistemas heredados) y PBKDF2 (cuando necesitas cumplir FIPS-140).
Firmas digitales: integridad + autenticidad + no repudio#
Un HMAC prueba autenticidad entre quienes comparten la clave, pero no sirve ante un tercero: cualquiera de los dos pudo generarlo. La firma digitalFirma digitalHash de un mensaje cifrado con la clave privada del emisor; prueba integridad, autenticidad y no repudio. rompe esa simetría usando criptografía asimétrica: el emisor firma con su clave privada y cualquiera verifica con su clave pública.
Paso 1 de 6El documento
El emisor quiere que el receptor confíe en un documento: que no se ha modificado y que viene de él. Firmarlo entero sería lento, así que trabaja sobre su hash.
El mecanismo es: se calcula el hash del documento y se “cifra” ese hash con la clave privada; eso es la firma. El verificador recalcula el hash y, con la clave pública, comprueba que coincide. Esto aporta tres garantías a la vez:
- Integridad: si el documento cambia, el hash cambia y la firma deja de validar.
- Autenticidad: solo el dueño de la clave privada pudo firmar.
- No repudioNo repudioGarantía de que alguien no puede negar de forma creíble haber realizado una acción, porque existe una evidencia fiable de ella.: como solo él tiene la privada, no puede negar de forma creíble que lo firmó. Es la propiedad que un HMAC no da.
Verás firmas en todas partes: los certificados TLS están firmados por una CA, los JWT de Cognito van firmados (RS256/ES256), la firma de código y de imágenes de contenedor, y los ficheros digest de CloudTrail, que van firmados para que puedas probar que los logs no se alteraron.
Tu turno
Elige la primitiva correcta para tres necesidades
Para cada caso, decide si usarías un hash a secas, un HMAC, una KDF de contraseñas o una firma digital, y por qué:
- Guardar las contraseñas de los usuarios de tu aplicación.
- Verificar que un fichero descargado de un espejo público no se ha corrompido.
- Que un webhook que recibes compruebe que viene realmente del proveedor y nadie lo manipuló por el camino.
- Distribuir un artefacto firmado para que cualquiera, sin compartir secreto contigo, pueda probar que lo publicaste tú.
Pista: fíjate en si hace falta una clave, si esa clave es compartida o un par público/privado, y si necesitas no repudio ante terceros.
Reto
¿Por qué no basta con publicar el SHA-256 de un instalador junto al enlace de descarga?
▸Mostrar solución
Porque si un atacante puede sustituir el instalador, probablemente también puede editar la página y sustituir el hash publicado. El hash protege frente a corrupción accidental, pero no frente a manipulación activa a menos que el hash venga por un canal de confianza firmado (por ejemplo, una firma del proveedor que verificas con su clave pública).
Reto
Soluciones del ejercicio
▸Mostrar solución
- KDF de contraseñas (Argon2id con sal): nunca un hash rápido ni cifrado. 2. Hash a secas (SHA-256): solo integridad frente a errores, el canal del hash debe ser fiable. 3. HMAC: tú y el proveedor compartís un secreto; prueba integridad y autenticidad entre ambos. 4. Firma digital: par público/privado, ofrece no repudio verificable por cualquiera.
En resumen#
- Una función hash (SHA-256) es de un solo sentido y prueba integridad; no es cifrado.
- MD5 y SHA-1 están rotos (colisiones); NIST retira SHA-1 para 2030 y ya lo prohíbe para firmas. Usa SHA-256/SHA-3.
- HMAC añade una clave compartida: integridad + autenticidad entre dos partes. Compara en tiempo constante.
- Las contraseñas van con sal + KDF lenta: Argon2id primero (scrypt, bcrypt, PBKDF2 como alternativas). Hash ≠ cifrado.
- La firma digital usa la clave privada sobre el hash y da integridad, autenticidad y no repudio verificables por cualquiera.
Referencias#
- NIST FIPS 180-4: Secure Hash Standard (SHA-2)
- NIST: Transitioning away from SHA-1 for all applications
- SHAttered: first SHA-1 collision · SHA-1 is a Shambles (chosen-prefix)
- OWASP Password Storage Cheat Sheet (Argon2id)
- NIST FIPS 198-1: The Keyed-Hash Message Authentication Code (HMAC)
- NIST FIPS 186-5: Digital Signature Standard (DSS)