DEV Community

Cover image for Por qué los hashes SHA-256 de contraseñas siguen siendo vulnerados
Passwork Team
Passwork Team

Posted on

Por qué los hashes SHA-256 de contraseñas siguen siendo vulnerados

Uno de los artículos de nuestro blog se ha mantenido consistentemente como el más popular: "Cómo funciona SHA-256: ¿Se puede desencriptar?". Decidimos traer algunas ideas de ese artículo a Dev.to, y esperamos que el tema anime a algunos de ustedes a cuestionar, refinar o ampliar estas ideas en los comentarios. Empecemos.

SHA-256 solo funciona en una dirección

"¿Se puede desencriptar SHA-256?" parece una pregunta razonable, pero mezcla dos operaciones distintas.

El cifrado está diseñado para ser reversible: los datos se transforman usando una clave, y quien tenga la clave adecuada puede recuperar el texto original. SHA-256 funciona de otra manera. Toma una entrada y produce un resumen (digest) de longitud fija, y el algoritmo solo calcula ese resumen hacia adelante.

Los hashes de contraseñas siguen siendo atacables por otra vía: la adivinación (guessing).

Supongamos que un atacante obtiene una base de datos con valores SHA-256(contraseña). Puede tomar una contraseña probable, aplicarle el hash y comparar el resultado con el valor almacenado. Una coincidencia revela la contraseña que produjo ese verificador.

El hashing y el cifrado resuelven problemas diferentes. La resistencia a la reversión bloquea un tipo de ataque. La adivinación sigue disponible como otro.

Qué hace SHA-256

SHA-256 acepta un mensaje de longitud arbitraria y produce un resumen determinista de 256 bits. Una entrada idéntica produce una salida idéntica, e incluso un pequeño cambio en la entrada modifica radicalmente el resumen resultante.

Internamente, SHA-256 rellena el mensaje, lo procesa en bloques de 512 bits, expande cada bloque en un calendario de mensajes (message schedule) y ejecuta una función de compresión durante 64 rondas. Para las decisiones sobre almacenamiento de contraseñas, una característica de diseño importa más que cualquier otra: SHA-256 está construido para calcular resúmenes de forma eficiente.

Esa eficiencia es útil. SHA-256 aparece en verificaciones de integridad, flujos de firma digital y otros sistemas donde procesar datos rápidamente es exactamente lo que los desarrolladores necesitan. Las contraseñas plantean un problema distinto.

Por qué el hashing rápido también ayuda a los atacantes

Durante un inicio de sesión normal, calcular un resumen SHA-256 con rapidez parece ideal. Después de una filtración de base de datos, esa misma velocidad beneficia al atacante.

Un atacante que trabaja sin conexión (offline) opera de forma independiente a tu aplicación. Los límites de frecuencia, la limitación de peticiones (throttling) de la API y los bloqueos de cuenta quedan fuera de juego, porque el atacante prueba contraseñas candidatas contra un verificador robado usando su propio hardware.

Para una función de hash de propósito general, hacer que cada cálculo sea económico es buena ingeniería. Para el almacenamiento de contraseñas, queremos que cada intento tenga un costo computacional significativo.

Por eso la Guía de almacenamiento de contraseñas de OWASP recomienda esquemas de hashing de contraseñas dedicados en lugar de funciones de hash rápidas de propósito general como SHA-256.

La distinción importante está entre un primitivo criptográfico y el sistema construido a su alrededor. SHA-256 puede seguir siendo una función de hash segura mientras que SHA-256(contraseña) sigue siendo un diseño de almacenamiento de contraseñas deficiente.

El salting y la derivación de claves resuelven problemas distintos

Añadir una sal (salt) es una mejora esencial, pero su propósito a veces se malinterpreta.

Una sal es un valor aleatorio único asociado a cada registro de contraseña. Dos usuarios que elijan la misma contraseña producen entonces valores almacenados diferentes. El salting también anula la precomputación reutilizable: un atacante no puede preparar una sola tabla de hashes de contraseñas comunes y aplicarla de forma eficiente en múltiples usuarios o bases de datos.

Una sal convierte cada registro de contraseña almacenado en un objetivo distinto. Hacer que cada intento sea costoso es un trabajo aparte, gestionado por una función de derivación de claves (KDF) para contraseñas.

La sal normalmente se guarda junto al verificador de la contraseña, así que en cuanto un atacante roba la base de datos, la conoce. Aun así puede tomar una contraseña candidata, combinarla con la sal de ese usuario, calcular el resultado y compararlo con el verificador almacenado.

Una KDF para contraseñas diseñada específicamente se encarga de ese segundo trabajo, incrementando deliberadamente el costo de cada intento. Argon2id y PBKDF2, por ejemplo, permiten a los sistemas ajustar la cantidad de trabajo necesaria para derivar un verificador.

Los controles resuelven problemas distintos:

Control Función principal Dónde queda corto
Sal aleatoria única Evita la precomputación compartida y hace que contraseñas idénticas generen hashes diferentes La velocidad de adivinación contra un registro conocido no cambia
Argon2id / PBKDF2 Hace que cada intento offline sea más costoso Una contraseña débil sigue siendo débil
Limitación de frecuencia + MFA Reduce el riesgo de apropiación de cuenta en línea Una base de hashes robada sigue necesitando su propia protección

Estos mecanismos se complementan entre sí. Cada uno cubre una parte distinta de la amenaza.

Qué usar en un sistema nuevo

Para una aplicación nueva, una opción por defecto razonable suele ser Argon2id. OWASP recomienda actualmente esta opción como la preferida para el hashing de contraseñas. Cuando aplican requisitos FIPS-140, OWASP recomienda PBKDF2-HMAC-SHA-256 con un factor de trabajo de al menos 600,000.

Trata PBKDF2-HMAC-SHA-256 como una construcción distinta de SHA-256 puro. SHA-256 puede formar parte de la construcción, pero PBKDF2 combina una función pseudoaleatoria con una sal y un factor de trabajo configurable, específicamente para hacer que las adivinaciones repetidas sean más costosas.

NIST SP 800-63B describe de forma similar el hashing de contraseñas en torno a sales y factores de costo, incrementando este último a medida que el rendimiento del sistema lo permite.

Trata esos parámetros como configuración: pon a prueba su rendimiento (benchmark) en tu infraestructura, documenta la decisión y revísala conforme evolucionen el hardware y las recomendaciones.

Las aplicaciones también necesitan una vía de actualización. Un framework o una biblioteca criptográfica mantenida activamente facilita esto más que implementar el esquema por cuenta propia. Almacena suficientes metadatos junto a cada verificador, incluyendo la versión del algoritmo, la sal, los parámetros y el verificador, para poder reconocer registros antiguos y migrarlos más adelante.

Más allá del hashing de contraseñas: los controles que aún necesitas

Una buena KDF para contraseñas cambia principalmente la economía de la adivinación offline después de que se ha robado una base de verificadores. Aborda un modelo de amenaza: la adivinación offline tras una filtración. Otras vías de ataque requieren controles independientes.

Los sistemas en producción todavía necesitan controles alrededor del flujo de inicio de sesión. La limitación de frecuencia incrementa el costo de la adivinación en línea. La MFA reduce el valor de una contraseña robada. Las políticas de contraseñas y las verificaciones contra contraseñas filtradas reducen las credenciales predecibles. La autenticación resistente al phishing aborda ataques que el hashing de contraseñas nunca llega a ver.

La planificación operativa también importa. Parámetros que parecían razonables hace varios años pueden llegar a ser económicos de calcular con el tiempo. Una estrategia de migración práctica consiste en detectar un verificador desactualizado tras un inicio de sesión exitoso y derivar uno nuevo usando el esquema y los parámetros actuales.

Para el almacenamiento de contraseñas, elige un esquema de hashing diseñado para ese propósito, como Argon2id o PBKDF2-HMAC-SHA-256. Usa bibliotecas mantenidas activamente, diseña pensando en actualizaciones de parámetros y trata la autenticación como un sistema: el hashing de contraseñas es solo uno de varios componentes.

SHA-256(contraseña) crea un problema de seguridad, mientras que SHA-256 en sí mismo sigue siendo criptográficamente sólido. La verdadera pregunta es cuán costoso hacemos cada intento del atacante una vez que la base de datos ya está fuera de nuestro control.

¿Qué esquema de hashing de contraseñas usa tu stack hoy en día, y cómo planificas y ejecutas las actualizaciones de parámetros o algoritmos como un despliegue gradual en lugar de una migración riesgosa de una sola vez?

Referencias

  1. Cómo funciona SHA-256: ¿Se puede desencriptar? — Passwork.
  2. OWASP Password Storage Cheat Sheet
  3. NIST SP 800-63B: Digital Identity Guidelines

Top comments (0)