Criptografía
Contraseñas y hash con Argon2id
Por qué los hashes rápidos fallan con contraseñas, qué hacen la sal y el factor de trabajo, y cómo hashear y verificar con Argon2id en Python.
Separa codificación, cifrado y hash con laboratorios de Base64, SHA-256 y OpenSSL. Entiende por qué Base64 no protege y el hash es de un solo sentido.
Codificación, cifrado y hash transforman los datos en otra cosa, y por eso se confunden. Tienen objetivos distintos. La codificación cambia la forma en que se escriben los datos, así que cualquiera puede revertirla. El cifrado oculta los datos a quien no tiene una clave, y se revierte con esa clave. El hash produce una huella corta que no se puede convertir de nuevo en la entrada original. Si recuerdas una sola frase: Base64 no es cifrado, y un hash no oculta nada en entradas cortas.
Este artículo separa las tres ideas y te pide ejecutar cada una en la terminal con el mismo texto. Así comparas los resultados tú mismo.
Necesitas una terminal con base64, sha256sum y openssl. En Windows 11, los comandos de abajo se ejecutan en Git Bash, que viene con Git para Windows. Incluyo también una versión en PowerShell para el paso del hash. No necesitas conocimientos previos de criptografía, aunque leer antes el artículo sobre el cifrado César ayuda a entender la idea de clave.
Todos los comandos del laboratorio son seguros de ejecutar en tu equipo. Usa un archivo de prueba, no un secreto real.
Las tres operaciones responden a tres preguntas diferentes.
| Operación | Pregunta que responde | ¿Necesita clave? | ¿Se puede revertir? |
|---|---|---|---|
| Codificación | ¿Cómo represento estos bytes como texto o en un formato que otro sistema espera? | No | Sí, por cualquiera |
| Cifrado | ¿Quién puede leer estos datos? | Sí | Sí, quien tenga la clave correcta |
| Hash | ¿Estos datos cambiaron, o dos valores coinciden? | No | No, por diseño |
Ten esta tabla presente durante el laboratorio. El resto del artículo explica cada fila.
La codificación reescribe los datos en otra forma para que un sistema pueda transportarlos. Base64 es el ejemplo clásico. Toma bytes y los escribe con 64 caracteres imprimibles: A a Z, a a z, 0 a 9, + y /, usando = como relleno. La RFC 4648 define el formato. Los adjuntos de correo, las data URLs y muchos payloads de API lo usan porque el canal espera texto.
Base64 no tiene ningún secreto. La tabla de conversión es pública y fija. Cualquier herramienta que conozca la regla puede convertir la salida otra vez en la entrada. Ese es el propósito de la codificación: interoperar, no mantener algo en secreto.
El cifrado toma datos legibles, llamados texto plano, y los transforma con un algoritmo y una clave en texto cifrado. Quien no tiene la clave no debería obtener nada útil del texto cifrado. Quien tiene la clave puede descifrarlo.
Hay dos familias. El cifrado simétrico usa la misma clave secreta para cifrar y descifrar. AES es la opción más común. El cifrado asimétrico usa una clave pública y una privada. Este artículo usa cifrado simétrico, porque el comando de OpenSSL del laboratorio también lo hace.
Un cifrado bueno depende de algo más que del algoritmo. Hay que elegir un modo seguro, generar las claves con una fuente aleatoria confiable y proteger la clave. El comando de OpenSSL del laboratorio usa una contraseña y PBKDF2 para derivar la clave. El texto de ayuda de OpenSSL indica que PBKDF2 usa 10.000 iteraciones por defecto, salvo que cambies ese valor con -iter.
Una función hash criptográfica recibe una entrada de cualquier longitud y devuelve una salida de longitud fija. SHA-256 está especificado en la FIPS 180-4, y su salida siempre tiene 256 bits, escritos como 64 caracteres hexadecimales. Dos propiedades importan para quien empieza:
El hash es de un solo sentido. Nada en la salida muestra el texto original. Puedes comprobar si una entrada candidata produce un hash conocido. No puedes recuperar la entrada solo a partir del hash, salvo adivinando y probando.
Recuerda que un hash es una huella, no un candado. Cualquiera puede calcular el SHA-256 de un archivo público. El hash demuestra que dos copias son iguales. No oculta el archivo.
La mayoría de los errores de seguridad reales en esta área vienen de usar la herramienta equivocada para el trabajo.
Entender esta diferencia desde el principio te evita estas trampas.
Abre Git Bash en una carpeta vacía. Codifica una frase corta y luego decodifica el resultado.
echo -n 'hello lab' | base64La salida que obtuve en este equipo, con OpenSSL 3.2.4 y Git Bash:
aGVsbG8gbGFiAhora decodifica esa salida:
echo -n 'aGVsbG8gbGFi' | base64 -dSalida:
hello labDos cosas para notar. Primero, no intervino ninguna clave. Segundo, echo -n importa. Sin -n, echo añade un salto de línea y la salida en Base64 cambia. Ejecuta echo 'hello lab' | base64 y compara: obtuve aGVsbG8gbGFiCg==. Aquí Base64 convierte 9 bytes en 12 caracteres, así que la salida es más larga que la entrada.
Pregúntate: ¿alguien sin ningún secreto podría revertir esto? Sí. Esa es la prueba que separa la codificación del cifrado.
Pasa la misma frase por SHA-256:
echo -n 'hello lab' | sha256sumSalida:
8fc6baba79a0d60b37dee612ccc1ec096ef52dba8cb0a24a6451e8bfb0e4d761 *-Ahora añade un signo de exclamación y vuelve a ejecutarlo:
echo -n 'hello lab!' | sha256sumSalida:
36902fb7fdea33458ad09dfeb52118dc47f99a8ca2b9001080366d55511de915 *-Los dos hashes no comparten ningún patrón visible. Un solo carácter cambió toda la salida. Ambos tienen 64 caracteres hexadecimales, es decir, 256 bits.
El - al final indica que la entrada vino de la entrada estándar, no de un archivo. El * anterior es la marca de modo binario que imprime el sha256sum de Git Bash. Otros sistemas pueden imprimir dos espacios, y el hash es el mismo. El hash de un archivo funciona igual.
Crea un archivo pequeño y calcula su hash:
echo 'secret note' > note.txt
sha256sum note.txtEn mi equipo, con saltos de línea estilo Unix en el archivo, la salida fue:
b5f891a5e064fec661025d0b0ec2de157487a6c2e5cc46dc0971fee56f86c645 *note.txtCambia el archivo, aunque sea un solo carácter, y vuelve a calcular el hash. El valor será otro. Así funcionan las comprobaciones de integridad. La comprobación te dice que el archivo cambió. No te dice qué cambió, y no impide que un atacante modifique el archivo y el hash publicado a la vez.
Si prefieres PowerShell, Windows tiene un comando integrado:
Get-FileHash note.txt -Algorithm SHA256Busca en la salida la línea Hash, que muestra 64 caracteres hexadecimales. El hash depende de los bytes exactos del archivo. Set-Content de PowerShell escribe saltos de línea de Windows (CRLF), que son distintos del final LF del ejemplo de Bash. En mi equipo, un archivo escrito así dio este hash:
406A5646AE75CA75D032F306ABD33085DC9F656E3109BB962AEAB335EEC9ED85Los mismos bytes calculados con sha256sum en Git Bash dieron el mismo valor, en minúsculas. Si tu hash difiere del ejemplo de Bash pero coincide con el resultado de PowerShell para tu propio archivo, la causa son los saltos de línea.
Crea una nota y cífrala con AES-256-CBC. OpenSSL te pedirá una contraseña. Elige una que recuerdes y úsala solo en este laboratorio.
echo 'secret note' > note.txt
openssl enc -aes-256-cbc -pbkdf2 -salt -in note.txt -out note.encOpenSSL pide la contraseña y el archivo cifrado se escribe en note.enc. La opción -salt añade una sal aleatoria, así que cifrar el mismo archivo dos veces produce salidas distintas. Un archivo cifrado con -salt empieza con el texto ASCII Salted__. Lo comprobé en mi equipo. El resto del archivo es binario e ilegible.
Ahora descífralo:
openssl enc -d -aes-256-cbc -pbkdf2 -in note.encIntroduce la misma contraseña. Salida:
secret noteEl descifrado solo funciona con la contraseña correcta. Probé con una incorrecta. OpenSSL mostró bad decrypt y terminó con error.
Después, cambia un byte del archivo cifrado e intenta descifrarlo. Invertí un bit dentro del bloque cifrado de una copia llamada tampered.enc. El descifrado falló con bad decrypt, y el comando terminó con estado 1.
Ese fallo es una casualidad del relleno del modo CBC. Otros cambios pueden producir basura sin ningún error. La lección es que el cifrado simple no da una señal fiable de que el texto cifrado fue modificado. Los sistemas que necesitan confidencialidad e integridad a la vez usan cifrado autenticado, como AES-GCM, o añaden un código de autenticación de mensajes. Es un tema avanzado, pero explica por qué un archivo cifrado no es automáticamente un archivo de confianza.
Pasa el mismo texto por cada operación y compara:
| Operación | Comando | Salida para hello lab (observada) |
|---|---|---|
| Codificación | echo -n 'hello lab' | base64 |
aGVsbG8gbGFi |
| Hash | echo -n 'hello lab' | sha256sum |
8fc6baba...e4d761 |
| Cifrado | openssl enc ... -in note.txt |
Datos binarios, necesitas la contraseña para leerlos |
Pregúntate qué te permite hacer cada salida:
hello lab.hello lab, pero no recuperar la entrada solo a partir del hash.secret note solo con la contraseña.Llamar "cifrado" u "ofuscación" a Base64 para secretos. Base64 no oculta nada. Quien vea una cadena en Base64 puede decodificarla. Guarda los secretos en un gestor de secretos o en un almacén cifrado, no en forma codificada.
Usar un hash como candado. Un hash es de un solo sentido, pero las entradas cortas o comunes se pueden adivinar. Calcula el hash de hello lab y un atacante con una lista de frases comunes lo recupera sin romper SHA-256. Por eso el hash de contraseñas necesita sal y algoritmos lentos.
Usar SHA-256 puro para contraseñas. SHA-256 es rápido por diseño, lo que sirve para sumas de verificación de archivos y no sirve para almacenar contraseñas. Usa una función de hash de contraseñas hecha para ese fin.
Confiar en un hash que viene del mismo lugar que el archivo. Un checksum en la misma página comprometida no demuestra nada. Obtén el hash esperado por un canal separado y confiable, o verifica una firma digital.
Olvidar que el cifrado exige gestión de claves. El laboratorio usa una contraseña escrita en un prompt. Los sistemas reales deben almacenar, rotar y proteger las claves. Perder la clave significa perder los datos.
Escribir tu propia criptografía. Usa bibliotecas y protocolos establecidos. Los comandos del laboratorio sirven para aprender, no son una plantilla para sistemas de producción.
Repite los tres laboratorios con otra frase y después con un archivo de unos pocos kilobytes. Verás que la salida en Base64 crece alrededor de un tercio, que el hash sigue teniendo 64 caracteres y que el archivo cifrado crece solo por la cabecera y el relleno.
Después, lee sobre hash de contraseñas. El problema allí no es el hash en sí, sino el costo de adivinar. El siguiente artículo de la serie, Cómo funciona el hash de contraseñas con Argon2id, explica la sal, el factor de trabajo y por qué importan los hashes lentos. Si antes quieres repasar el lado de las cifras, lee Cifrado César en Python. Muestra una clave y un algoritmo en una forma que puedes probar.
Cuando estudies protocolos reales, busca tres cosas en cada diseño: qué codifica, qué cifra y qué calcula como hash. La mayor parte de la confusión desaparece cuando preguntas cuál de los tres trabajos está haciendo cada parte.
Criptografía
Por qué los hashes rápidos fallan con contraseñas, qué hacen la sal y el factor de trabajo, y cómo hashear y verificar con Argon2id en Python.
Criptografía
Claves pública y privada desde cero: qué prueba una firma, por qué la privada no sale de su dueño y un lab Ed25519 donde un archivo alterado falla.
Criptografía
Criptografía desde cero: cifrado, claves, hashes, firmas digitales y por qué el cifrado César enseña ideas pero no protege nada.