crypt es una función de la biblioteca POSIX C. Normalmente se utiliza para calcular el hash de las contraseñas de las cuentas de usuario. La función genera una cadena de texto que también codifica el salt (normalmente los dos primeros caracteres son el salt en sí y el resto es el resultado hash) e identifica el algoritmo hash utilizado (por defecto el "tradicional" que se explica a continuación). Esta cadena de salida forma un registro de contraseña, que normalmente se almacena en un archivo de texto.
Más formalmente, crypt proporciona funciones de derivación de claves criptográficas para la validación y el almacenamiento de contraseñas en sistemas Unix.
Existe una utilidad crypt no relacionada en Unix, que a menudo se confunde con la función de biblioteca C. Para distinguir entre los dos, los escritores a menudo se refieren al programa de utilidad como crypt(1) , porque está documentado en la sección 1 de las páginas del manual de Unix , y se refieren a la función de la biblioteca C como crypt(3) , porque su documentación está en manual sección 3. [1]
Esta misma función de cripta se utiliza tanto para generar un nuevo hash para almacenamiento como también para codificar una contraseña ofrecida con un salt registrado para comparar.
Las implementaciones modernas de Unix de la rutina de la biblioteca crypt admiten una variedad de esquemas hash. El algoritmo hash particular utilizado puede identificarse mediante un prefijo de código único en el texto hash resultante, siguiendo un estándar de facto llamado formato de cripta modular. [2] [3] [4]
La crypt()
función de biblioteca también se incluye en los lenguajes de programación Perl , [5] PHP , [6] Pike , [7] Python [8] (aunque ahora está obsoleto a partir de 3.11) y Ruby [9] .
Con el tiempo se han introducido varios algoritmos. Para permitir la compatibilidad con versiones anteriores , cada esquema comenzó a utilizar alguna convención de serialización de los hashes de contraseñas que más tarde se denominó Formato de Cripta Modular (MCF). [3] Los hashes antiguos de crypt(3) generados antes del estándar MCF de facto pueden variar de un esquema a otro. Durante el concurso de hash de contraseñas se creó un subconjunto bien definido del formato de cripta modular . [3] El formato se define como: [10]
$<id>[$<param>=<value>(,<param>=<value>)*][$<salt>[$<hash>]]
dónde
id
: un identificador que representa el algoritmo hash (como 1 para MD5 , 5 para SHA-256 , etc.)param
nombre y su value
: parámetros de complejidad hash, como el recuento de rondas/iteracionessalt
: sal siguiendo el alfabeto radix-64 (DES usa el valor decodificado)hash
: resultado codificado en radix-64 del hash de la contraseña y la salLa codificación radix-64 en crypt se llama B64 y usa el alfabeto ./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
que es diferente al RFC 4648 base64 más común.
El subconjunto PHC cubre la mayoría de los hashes de MCF. Existen varios métodos adicionales definidos por la aplicación. [3]
La implementación original de la función de biblioteca crypt() [11] en la tercera edición de Unix [12] imitaba la máquina de cifrado M-209 . En lugar de cifrar la contraseña con una clave, lo que habría permitido recuperar la contraseña a partir del valor cifrado y la clave, utilizó la contraseña misma como clave, y la base de datos de contraseñas contenía el resultado de cifrar la contraseña con esta clave.
Se descubrió que el esquema de cifrado de contraseña original era demasiado rápido y, por lo tanto, estaba sujeto a una enumeración de fuerza bruta de las contraseñas más probables. [11] En la séptima edición de Unix , [13] el esquema se cambió a una forma modificada del algoritmo DES . Uno de los objetivos de este cambio era hacer que el cifrado fuera más lento. Además, el algoritmo incorporó un salt de 12 bits para garantizar que un atacante se viera obligado a descifrar cada contraseña de forma independiente en lugar de poder atacar toda la base de datos de contraseñas simultáneamente.
En detalle, la contraseña del usuario se trunca a ocho caracteres, y estos se reducen a sólo 7 bits cada uno; esto forma la clave DES de 56 bits. Luego, esa clave se usa para cifrar un bloque de bits cero, y luego el texto cifrado se cifra nuevamente con la misma clave, y así sucesivamente hasta un total de 25 cifrados DES. Se utiliza una sal de 12 bits para perturbar el algoritmo de cifrado, por lo que las implementaciones DES estándar no se pueden utilizar para implementar crypt(). La sal y el texto cifrado final se codifican en una cadena imprimible en forma de base64 .
Técnicamente, esto no es cifrado ya que los datos (todos los bits cero) no se mantienen en secreto; es ampliamente conocido por todos de antemano. Sin embargo, una de las propiedades de DES es que es muy resistente a la recuperación de claves incluso ante situaciones conocidas de texto sin formato . En teoría, es posible que dos contraseñas diferentes den como resultado exactamente el mismo hash. Por lo tanto, la contraseña nunca se "descifra": simplemente se utiliza para calcular un resultado, y se supone que los resultados coincidentes son prueba de que las contraseñas eran "iguales".
Las ventajas de este método han sido que el texto hash se puede almacenar y copiar entre sistemas Unix sin exponer la contraseña de texto plano correspondiente a los administradores del sistema u otros usuarios. Esta portabilidad ha funcionado durante más de 30 años en muchas generaciones de arquitectura informática y en muchas versiones de Unix de muchos proveedores.
El algoritmo de cifrado tradicional basado en DES se eligió originalmente porque DES era resistente a la recuperación de claves incluso frente a ataques de "texto sin formato conocido" y porque era computacionalmente costoso. En las primeras máquinas Unix, se necesitaba más de un segundo para calcular un hash de contraseña. Esto también lo hizo razonablemente resistente a los ataques de diccionario en esa época. En ese momento, los hashes de contraseñas se almacenaban comúnmente en un archivo de cuenta ( /etc/passwd
) que cualquiera en el sistema podía leer. (Este archivo de cuenta también se usó para asignar números de identificación de usuario a nombres y nombres de usuarios a nombres completos, etc.).
En las tres décadas transcurridas desde entonces, las computadoras se han vuelto mucho más poderosas. La Ley de Moore generalmente se ha cumplido, por lo que la velocidad y la capacidad de la computadora disponible para una determinada inversión financiera se han duplicado más de 20 veces desde que se escribió Unix por primera vez. Esto hace tiempo que dejó al algoritmo basado en DES vulnerable a ataques de diccionario, y Unix y sistemas similares a Unix como Linux han usado archivos "sombra" durante mucho tiempo, migrando sólo los valores hash de contraseña fuera del archivo de cuenta ( /etc/passwd
) y en un archivo (convencionalmente llamado /etc/shadow
) que sólo puede ser leído por procesos privilegiados.
Para aumentar el costo computacional de descifrar contraseñas, algunos sitios Unix comenzaron de forma privada a aumentar el número de rondas de cifrado de forma ad hoc. [ cita necesaria ] Esto tuvo el efecto secundario de hacerlos crypt()
incompatibles con el estándar crypt()
: los hashes tenían la misma forma textual, pero ahora se calculaban utilizando un algoritmo diferente. Algunos sitios también aprovecharon este efecto de incompatibilidad modificando el bloque inicial del estándar de todos los bits cero. [ cita necesaria ] Esto no aumentó el costo del hash, pero significó que no se podían aplicar diccionarios hash precalculados basados en el estándar crypt() .
BSDi utilizó una ligera modificación del esquema clásico basado en DES. BSDi extendió la sal a 24 bits e hizo que el número de rondas fuera variable (hasta 2 · 24 -1). El número de rondas elegido se codifica en el hash de la contraseña almacenada, evitando la incompatibilidad que se produjo cuando los sitios modificaron el número de rondas utilizadas por el esquema original. Estos hashes se identifican comenzando con un guión bajo ( _
), seguido de 4 caracteres que representan el número de rondas y luego 4 caracteres para la sal.
El algoritmo BSDi también admite contraseñas más largas, utilizando DES para reducir la contraseña larga inicial a los ocho bytes de 7 bits admitidos por el algoritmo original.
Poul-Henning Kamp diseñó un algoritmo barroco y (en ese momento) computacionalmente costoso basado en el algoritmo de resumen de mensajes MD5 . El propio MD5 proporcionaría una buena solidez criptográfica para el hash de la contraseña, pero está diseñado para que su cálculo sea bastante rápido en relación con la solidez que proporciona. El esquema crypt() está diseñado para que su cálculo sea costoso y ralentice los ataques de diccionario. La forma imprimible de hash de contraseña MD5 comienza con $1$
.
Este esquema permite a los usuarios tener contraseñas de cualquier longitud y pueden usar cualquier carácter admitido por su plataforma (no solo ASCII de 7 bits). (En la práctica, muchas implementaciones limitan la longitud de la contraseña, pero generalmente admiten contraseñas mucho más largas de lo que cualquier persona estaría dispuesta a escribir). La sal también es una cadena arbitraria, limitada únicamente por consideraciones del conjunto de caracteres.
Primero, se combinan la frase de contraseña y la sal, lo que genera un resumen del mensaje MD5. Luego se construye un nuevo resumen, combinando la frase de contraseña, la sal y el primer resumen, todo en una forma bastante compleja. Luego, este resumen pasa por mil iteraciones de una función que lo repite junto con la frase de contraseña y la sal de una manera que varía entre rondas. El resultado de la última de estas rondas es el hash de frase de contraseña resultante.
El recuento fijo de iteraciones ha provocado que este esquema pierda el gasto computacional que antes disfrutaba y ahora se favorece un número variable de rondas. En junio de 2012, Poul-Henning Kamp declaró que el algoritmo era inseguro y alentó a los usuarios a migrar a codificadores de contraseñas más potentes. [14]
Niels Provos y David Mazières diseñaron un esquema crypt() llamado bcrypt basado en Blowfish y lo presentaron en USENIX en 1999. [15] La forma imprimible de estos hashes comienza con , , o dependiendo de qué variante $2$
del $2a$
algoritmo $2b$
se utilice:$2x$
$2y$
$2$
- Obsoleto.$2a$
– La clave actual utilizada para identificar este esquema. Desde que se descubrió una falla de seguridad importante en 2011 en una implementación del algoritmo crypt_blowfish que no es de OpenBSD, [16] los hashes indicados por esta cadena ahora son ambiguos y podrían haber sido generados por la implementación defectuosa o por una implementación reparada posterior. La falla puede ser provocada por algunas cadenas de contraseña que contienen caracteres que no son ASCII (conjunto de 8 bits).$2b$
– Utilizado por implementaciones recientes de OpenBSD para incluir una mitigación de un problema envolvente. [17] Las versiones anteriores del algoritmo tienen un problema con las contraseñas largas. Por diseño, las contraseñas largas se truncan a 72 caracteres, pero existe un problema de envoltura de bytes enteros con ciertas longitudes de contraseña que dan como resultado hashes débiles. [18]$2x$
– Una bandera agregada después del descubrimiento del error crypt_blowfish . Se puede cambiar el nombre de los hashes antiguos para indicar que se generaron con el algoritmo roto. Estos hashes aún son débiles, pero al menos está claro qué algoritmo se utilizó para generarlos.$2x$
$2y$
– Una bandera en crypt_blowfish para utilizar sin ambigüedades el nuevo algoritmo corregido. En una implementación anterior que sufre el error, simplemente no funcionará. En una implementación más nueva y fija, producirá el mismo resultado que usar .$2y$
$2b$
Blowfish se destaca entre los cifrados en bloque por su costosa fase de configuración de claves. Comienza con subclaves en un estado estándar, luego usa este estado para realizar un cifrado en bloque usando parte de la clave y usa el resultado de ese cifrado (en realidad, un hash) para reemplazar algunas de las subclaves. Luego usa este estado modificado para cifrar otra parte de la clave y usa el resultado para reemplazar más subclaves. Procede de esta manera, utilizando un estado modificado progresivamente para codificar la clave y reemplazar bits de estado, hasta que se hayan configurado todas las subclaves.
El número de rondas de codificación es una potencia de dos, que es una entrada para el algoritmo. El número está codificado en el hash textual, por ejemplo$2y$10...
FreeBSD implementó soporte para el algoritmo hash de NT LAN Manager para proporcionar una compatibilidad más sencilla con cuentas NT a través de MS-CHAP . [19] Se sabe que el algoritmo NT-Hash es débil, ya que utiliza el algoritmo hash md4 obsoleto sin ningún tipo de sal. [20] FreeBSD usó el $3$
prefijo para esto. No se recomienda su uso, ya que se rompe fácilmente. [1]
El esquema basado en MD5 comúnmente utilizado se ha vuelto más fácil de atacar a medida que aumenta la potencia de la computadora. Aunque el sistema basado en Blowfish tiene la opción de agregar rondas y, por lo tanto, sigue siendo un algoritmo de contraseña desafiante, no utiliza un algoritmo aprobado por NIST . A la luz de estos hechos, Ulrich Drepper de Red Hat lideró un esfuerzo para crear un esquema basado en las funciones hash SHA-2 (SHA-256 y SHA-512). [21] La forma imprimible de estos hash comienza con $5$
(para SHA-256) o $6$
(para SHA-512) dependiendo de qué variante SHA se utilice. Su diseño es similar a la cripta basada en MD5, con algunas diferencias notables: [21]
La especificación y el código de muestra se han hecho públicos; a menudo se le conoce como "SHAcrypt". [24]
$y$
$7$
) y finalista de PHC. Se utiliza en varias distribuciones de Linux como alternativa a los esquemas existentes. [25] Para usar este hash, el libcrypt
de glibc se reemplaza por uno compatible con versiones anteriores del proyecto "libxcrypt". [26]$argon2d$
, $argon2i$
,$argon2ds$
Los formatos adicionales, si los hay, se describen en las páginas de manual de las implementaciones. [27]
BigCrypt es la versión modificada de DES-Crypt utilizada en HP-UX, Digital Unix y OSF/1. La principal diferencia entre este y DES es que BigCrypt usa todos los caracteres de una contraseña, no solo los primeros 8, y tiene un hash de longitud variable. [28]
Crypt16 es la modificación menor de DES, que permite contraseñas de hasta 16 caracteres. Utilizado en Ultrix y Tru64. [29]
La biblioteca GNU C utilizada por casi todas las distribuciones de Linux proporciona una implementación de la función crypt que admite los algoritmos hash basados en DES, MD5 y (desde la versión 2.7) SHA-2 mencionados anteriormente. Ulrich Drepper, el mantenedor de glibc, rechazó el soporte de bcrypt (esquema 2) ya que no está aprobado por NIST . [31] Una biblioteca crypt_blowfish de dominio público está disponible para sistemas sin bcrypt. Se ha integrado en glibc en SUSE Linux . [32] Además, el libxcrypt mencionado anteriormente se utiliza para reemplazar glibc crypt() en sistemas habilitados para yescrypt.
La biblioteca musl C admite los esquemas 1, 2, 5 y 6, además del esquema tradicional DES. El código DES tradicional se basa en BSD FreeSec , con modificaciones para que sea compatible con glibc UFC-Crypt . [33]
El nativo de Darwin crypt()
proporciona una funcionalidad limitada y solo admite DES y BSDi. OS X utiliza algunos sistemas para sus propios hashes de contraseñas, desde el antiguo NeXTStep netinfo hasta el nuevo sistema de servicios de directorio (ds). [34] [35]