Tema 5

5. Contraseñas seguras, hashing, salting y almacenamiento de credenciales

Las contraseñas siguen siendo uno de los mecanismos de autenticación más usados, pero también uno de los más atacados. La diferencia entre un sistema razonablemente seguro y uno expuesto suele estar menos en el formulario de login que en cómo se generan, protegen, verifican y almacenan las credenciales.

Objetivo Entender cómo proteger contraseñas correctamente
Enfoque Práctico, técnico y orientado a implementación segura
Resultado Saber qué almacenar y qué nunca almacenar

5.1 Introducción

En la mayoría de los sistemas, el usuario percibe la contraseña como un simple dato que se escribe para ingresar. Desde el punto de vista de seguridad, en cambio, la contraseña es un secreto de alto valor que puede abrir la puerta a datos, operaciones sensibles y movimientos laterales hacia otros servicios.

Por eso el problema no se limita a pedir una clave “fuerte”. Un diseño serio debe contemplar cómo se crea, cómo se transmite, cómo se verifica, cómo se almacena, cómo se resetea y qué ocurre si la base de datos es comprometida.

Este tema se centra en una idea fundamental: un sistema no debería guardar contraseñas de forma recuperable. Debe guardar evidencia verificable derivada de ellas, usando algoritmos adecuados y parámetros resistentes al hardware moderno.

5.2 Por qué las contraseñas siguen siendo problemáticas

Las contraseñas son populares porque son simples de entender, baratas de implementar y compatibles con casi cualquier dispositivo. Sin embargo, presentan debilidades estructurales:

  • Los usuarios tienden a elegir secretos débiles o reutilizados.
  • Son vulnerables a phishing, keylogging y malware.
  • Pueden filtrarse en brechas y reutilizarse en otros servicios.
  • Su gestión genera fricción, olvidos y costos de soporte.
  • Su seguridad depende tanto del diseño técnico como del comportamiento humano.

Eso no significa que haya que abandonarlas automáticamente en todos los contextos, pero sí tratarlas con extremo cuidado y complementarlas cuando el riesgo lo justifique.

5.3 Qué significa una contraseña segura

Hablar de “contraseña segura” no implica solo longitud o símbolos extraños. Una contraseña es más segura cuando tiene suficiente entropía práctica y no puede adivinarse fácilmente por patrones comunes, información personal o reutilización.

En términos generales, una buena contraseña o frase de paso debería:

  • Ser suficientemente larga.
  • No reutilizarse entre servicios.
  • No derivarse de datos obvios del usuario.
  • No depender de patrones demasiado predecibles.
  • Idealmente almacenarse en un gestor de contraseñas, no en la memoria del usuario como único apoyo.

En muchos entornos modernos se favorecen contraseñas largas y recordables o frases de paso, antes que reglas artificiales que empujan a los usuarios a usar variantes débiles de una misma clave.

5.4 Reglas de complejidad versus longitud y usabilidad

Durante años fue común exigir una mezcla rígida de mayúsculas, minúsculas, números y símbolos, junto con cambios frecuentes obligatorios. Hoy ese enfoque se revisa críticamente porque muchas veces produce malos resultados.

Cuando las reglas son muy artificiales, los usuarios suelen responder con patrones previsibles: reemplazos triviales, sufijos incrementales o reutilización de fórmulas fáciles de recordar. Eso da una ilusión de seguridad, pero no necesariamente mejora la resistencia real frente a ataques.

Por eso muchas recomendaciones actuales priorizan:

  • Longitud suficiente.
  • Bloqueo de contraseñas conocidas por filtraciones o extremadamente comunes.
  • Evitar rotaciones forzadas salvo indicio de compromiso.
  • Apoyo con gestores de contraseñas y MFA.

5.5 Qué nunca debe hacerse al almacenar contraseñas

Hay errores que siguen apareciendo y son inaceptables en un sistema moderno:

  • Guardar contraseñas en texto plano.
  • Guardar contraseñas cifradas con una clave reversible como si eso fuera equivalente a hash.
  • Usar funciones hash rápidas como MD5 o SHA-1 para almacenamiento de contraseñas.
  • Usar el mismo salt para todos los usuarios o no usar salt.
  • Diseñar procesos donde el sistema pueda “recordar” la contraseña original del usuario.
Si una aplicación puede mostrarle al usuario su contraseña actual, entonces esa contraseña está almacenada de forma recuperable. Eso es una señal clara de diseño inseguro.

5.6 Texto plano, cifrado reversible y hash: no son lo mismo

Conviene distinguir tres enfoques muy diferentes:

Método Cómo funciona ¿Es adecuado para contraseñas?
Texto plano Se guarda exactamente la contraseña No, es gravemente inseguro
Cifrado reversible Se puede recuperar la contraseña con una clave No como almacenamiento principal de password
Hash Se guarda un derivado unidireccional verificable Sí, si se usa un algoritmo adecuado

El objetivo del hash es que, al momento del login, el sistema pueda recalcular el derivado de la contraseña ingresada y compararlo con el valor almacenado, sin necesidad de conocer la contraseña original.

5.7 Qué es un hash de contraseña

Un hash es el resultado de aplicar una función unidireccional a una entrada. En el caso de contraseñas, el sistema toma la contraseña provista por el usuario, le aplica un algoritmo de derivación y almacena el resultado.

Luego, cuando el usuario intenta autenticarse, el sistema repite el proceso con la contraseña ingresada y compara ambos resultados. Si coinciden, se considera válida.

La idea clave es que el hash no debería permitir reconstruir directamente la contraseña original. Pero eso por sí solo no alcanza. Para contraseñas no basta con “cualquier hash”; hace falta un hash pensado específicamente para resistir ataques de fuerza bruta offline.

5.8 Por qué MD5, SHA-1 o SHA-256 no son suficientes para passwords

Funciones como MD5, SHA-1 o incluso SHA-256 son útiles para integridad, firmas o checksums en ciertos contextos, pero no son apropiadas como mecanismo principal para almacenar contraseñas.

El problema no es que sean “hash” y por eso alcancen, sino que son demasiado rápidas. Un atacante con acceso a una base filtrada puede probar enormes cantidades de combinaciones por segundo usando hardware especializado. Cuanto más rápido sea el algoritmo, más viable resulta el ataque offline.

Para contraseñas se necesitan algoritmos deliberadamente costosos en tiempo y, preferentemente, también en memoria. Esa lentitud controlada protege al defensor porque encarece el ataque masivo.

5.9 Algoritmos adecuados: bcrypt, scrypt y Argon2

Los algoritmos recomendados para almacenamiento de contraseñas son aquellos diseñados específicamente para este problema. Los más citados hoy son:

  • bcrypt: ampliamente soportado y todavía válido si se configura correctamente.
  • scrypt: incorpora resistencia basada en consumo de memoria.
  • Argon2: especialmente Argon2id, considerado por muchos el estándar moderno más sólido.

La elección concreta depende del entorno, las bibliotecas disponibles y los requisitos operativos, pero la idea es la misma: usar un algoritmo lento, parametrizable y pensado para contraseñas, no una función hash genérica.

5.10 Qué es un salt y por qué es imprescindible

Un salt es un valor aleatorio único que se combina con la contraseña antes de derivar el hash. Su propósito principal es evitar que dos usuarios con la misma contraseña terminen con el mismo valor almacenado y dificultar ataques basados en tablas precalculadas.

Si no existiera salt, una base comprometida mostraría patrones peligrosos:

  • Dos usuarios con la misma contraseña tendrían el mismo hash.
  • Podrían reutilizarse rainbow tables o diccionarios precalculados.
  • El atacante obtendría información estructural incluso antes de romper contraseñas.

El salt no necesita ser secreto, pero sí único por credencial y generado con suficiente aleatoriedad.

5.11 Qué es un pepper y cuándo puede ayudar

Además del salt, a veces se utiliza un pepper: un secreto adicional no almacenado junto a la base de datos de usuarios, sino en un entorno separado y protegido, como un servicio de secretos o un módulo seguro.

La idea es agregar una capa extra para que una filtración de la base por sí sola no alcance para verificar masivamente contraseñas. Sin embargo, el pepper no reemplaza ni al salt ni al uso de algoritmos adecuados. Es una medida complementaria.

También introduce complejidad operativa: rotación, disponibilidad y gestión segura del secreto. Por eso debe usarse con criterio y no como sustituto de prácticas básicas bien implementadas.

5.12 Cost factor y parametrización

Los algoritmos de password hashing modernos permiten ajustar parámetros para controlar su costo. Ese ajuste es crítico. Un algoritmo correcto con parámetros demasiado débiles puede terminar siendo insuficiente frente al hardware actual.

En general, el objetivo es que la verificación legítima siga siendo aceptable para el sistema y el usuario, pero que un ataque masivo offline resulte costoso. No existe un número universal: debe calibrarse según la infraestructura, el volumen de autenticaciones y la evolución tecnológica.

Esto implica revisar periódicamente los parámetros. La seguridad de hoy puede quedar desactualizada mañana si la capacidad de cómputo cambia o si el sistema lleva muchos años sin ajuste.

5.13 Flujo correcto de registro y verificación

Un flujo básico seguro para contraseñas debería verse así:

  1. El usuario elige una contraseña.
  2. El sistema valida políticas mínimas y controles de calidad.
  3. Se genera un salt único.
  4. Se deriva un hash usando un algoritmo adecuado y parámetros actuales.
  5. Se almacenan el hash, el salt y los metadatos necesarios del algoritmo.
  6. En el login, se repite el proceso con la contraseña ingresada y se compara el resultado.

En ningún punto del flujo el sistema necesita almacenar ni recuperar la contraseña en texto original.

5.14 Cómo impacta una brecha de base de datos

Uno de los objetivos del almacenamiento correcto es reducir el daño si la base de usuarios se filtra. Si las contraseñas están en texto plano o cifradas reversiblemente, la brecha es catastrófica e inmediata. Si están protegidas con algoritmos adecuados y buenos parámetros, el atacante todavía enfrenta un trabajo costoso para romper cada credencial.

Eso no elimina el incidente, pero cambia radicalmente el escenario. Da tiempo para revocar sesiones, forzar reseteos, investigar y contener impacto antes de que una gran cantidad de cuentas queden efectivamente comprometidas.

El almacenamiento seguro de contraseñas no evita que una base sea robada. Lo que hace es reducir el valor explotable inmediato de esa base.

5.15 Reseteo de contraseñas y recuperación de acceso

Un sistema puede tener hashing impecable y aun así ser débil si el proceso de recuperación de cuenta es inseguro. Muchas brechas evitan el login normal y atacan directamente el reseteo.

Buenas prácticas en esta área incluyen:

  • No enviar la contraseña actual por correo ni mostrarla en ningún canal.
  • Enviar enlaces o tokens de reseteo de un solo uso, con expiración corta.
  • Proteger el flujo con controles anti-abuso y registro de eventos.
  • Evitar preguntas secretas débiles como mecanismo principal.
  • Notificar al usuario cuando se inicia o completa un proceso de cambio.

5.16 Reutilización, listas de contraseñas comprometidas y políticas modernas

Una contraseña puede ser robusta en abstracto y aun así estar comprometida si ya apareció en filtraciones previas. Por eso muchos sistemas modernos validan nuevas contraseñas contra listas de secretos conocidos como expuestos o demasiado comunes.

Esta práctica es más útil que imponer reglas arbitrarias de símbolos si permite bloquear claves previsibles o recicladas masivamente. También es importante educar al usuario para no reutilizar la misma contraseña entre servicios, especialmente si no utiliza un gestor de contraseñas.

5.17 Contraseñas y MFA: relación, no reemplazo automático

La presencia de MFA mejora mucho el panorama, pero no autoriza a descuidar las contraseñas. Si el factor principal es débil, el sistema sigue expuesto a múltiples formas de abuso: phishing parcial, password spraying, soporte fraudulento, takeover de cuentas con segundo factor comprometido o flujos alternativos mal resueltos.

La contraseña debe seguir tratándose como un secreto de alto valor, aunque luego exista MFA. La defensa en profundidad exige proteger ambos niveles, no confiar en que uno compensa totalmente la fragilidad del otro.

5.18 Errores frecuentes en la implementación

  • Guardar passwords en texto plano o de forma reversible.
  • Usar SHA-256 o MD5 directamente para almacenar contraseñas.
  • No usar salt único por usuario.
  • Elegir parámetros demasiado bajos por comodidad o inercia histórica.
  • No actualizar algoritmos o configuraciones con el paso del tiempo.
  • Diseñar un reset inseguro que invalide toda la protección del almacenamiento.
  • Permitir contraseñas triviales, filtradas o demasiado comunes.

5.19 Qué debes recordar de este tema

  • Las contraseñas no deben almacenarse en texto plano ni de forma reversible.
  • Para passwords se deben usar algoritmos de derivación específicos como bcrypt, scrypt o Argon2.
  • El salt único por credencial es imprescindible.
  • Funciones hash rápidas como MD5 o SHA-256 no son suficientes para este caso de uso.
  • El proceso de recuperación de cuenta es tan importante como el almacenamiento.
  • La seguridad de contraseñas depende de diseño técnico, políticas razonables y operación continua.

5.20 Conclusión

Las contraseñas siguen presentes en gran parte de los sistemas, pero su seguridad no depende de slogans sobre complejidad. Depende de decisiones concretas: qué algoritmos se usan, cómo se parametrizan, cómo se almacenan los derivados, cómo se gestionan los resets y cómo se reduce el impacto de una eventual filtración.

En el próximo tema veremos autenticación multifactor con más detalle: sus variantes, sus beneficios reales y los criterios para implementarla sin caer en una falsa sensación de seguridad.