crypt es una función de la biblioteca POSIX C. Se utiliza normalmente 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 la sal (normalmente los dos primeros caracteres son la sal en sí y el resto es el resultado del hash) e identifica el algoritmo de 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 las 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 biblioteca C como crypt(3) , porque su documentación está en la sección 3 del manual. [1]
Esta misma función de cifrado se utiliza tanto para generar un nuevo hash para almacenamiento como para hacer un hash de una contraseña propuesta con una sal registrada para comparación.
Las implementaciones modernas de Unix de la rutina de biblioteca de cifrado admiten una variedad de esquemas hash. El algoritmo hash particular utilizado se puede identificar mediante un prefijo de código único en el texto hash resultante, siguiendo un estándar de facto llamado Formato de cifrado modular. [2] [3] [4]
La crypt()
función de biblioteca también está incluida en los lenguajes de programación Perl , [5] PHP , [6] Pike , [7] Python [8] (aunque ahora está obsoleta a partir de la versión 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 cifrado modular (MCF). [3] Los hashes crypt(3) antiguos generados antes del estándar MCF de facto pueden variar de un esquema a otro. Un subconjunto bien definido del Formato de cifrado modular se creó durante la Competencia de cifrado de contraseñas . [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 sus value
parámetros de complejidad hash, como el recuento de rondas/iteracionessalt
: sal que sigue el alfabeto de base 64 (DES utiliza 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 utiliza un alfabeto ./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
diferente al más común RFC 4648 base64.
El subconjunto PHC cubre la mayoría de los hashes MCF. Existe una serie de 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 recuperarla a partir del valor cifrado y la clave, utilizaba la propia contraseña 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ñas original era demasiado rápido y, por lo tanto, estaba sujeto a una enumeración por 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 . Un objetivo de este cambio era hacer que el cifrado fuera más lento. Además, el algoritmo incorporó una sal 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 solo 7 bits cada uno; esto forma la clave DES de 56 bits. Esa clave se utiliza luego para cifrar un bloque de todos los bits a cero, y luego el texto cifrado se vuelve a cifrar con la misma clave, y así sucesivamente para un total de 25 cifrados DES. Se utiliza una sal de 12 bits para perturbar el algoritmo de cifrado, por lo que las implementaciones estándar de DES 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 son cero) no se mantienen en secreto; todos los conocen de antemano. Sin embargo, una de las propiedades de DES es que es muy resistente a la recuperación de claves, incluso en situaciones de texto plano conocido . En teoría, es posible que dos contraseñas diferentes den exactamente el mismo hash. Por lo tanto, la contraseña nunca se "descifra": simplemente se utiliza para calcular un resultado, y se presume que los resultados coincidentes son una prueba de que las contraseñas eran "las mismas".
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 en texto simple 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 ante ataques de "texto plano conocido" y porque era costoso en términos computacionales. En las primeras máquinas Unix, se necesitaba más de un segundo para calcular un hash de contraseña. Esto también lo hacía 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 era legible para cualquier persona en el sistema. (Este archivo de cuenta también se usaba para mapear números de ID de usuario en nombres, y nombres de usuario en nombres completos, etc.).
En las tres décadas transcurridas desde entonces, las computadoras se han vuelto mucho más potentes. La Ley de Moore se ha mantenido en general cierta, por lo que la velocidad y la capacidad de las computadoras disponibles para una inversión financiera determinada se han duplicado más de 20 veces desde que se escribió Unix por primera vez. Esto ha dejado desde hace mucho tiempo al algoritmo basado en DES vulnerable a ataques de diccionario, y Unix y los sistemas similares a Unix, como Linux, han utilizado archivos "shadow" durante mucho tiempo, migrando solo los valores hash de contraseñas del archivo de cuenta ( /etc/passwd
) a un archivo (convencionalmente llamado /etc/shadow
) que solo pueden leer los procesos privilegiados.
Para aumentar el costo computacional de descifrar contraseñas, algunos sitios Unix comenzaron a aumentar de manera privada el número de rondas de cifrado de manera ad hoc. [ cita requerida ] 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 all-bits-zero. [ cita requerida ] Esto no aumentó el costo del hash, pero significó que los diccionarios de hashes precalculados basados en el estándar crypt() no se podían aplicar.
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 contraseña almacenado, evitando la incompatibilidad que se producía cuando los sitios modificaban el número de rondas utilizado 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 su momento) computacionalmente costoso basado en el algoritmo de resumen de mensajes MD5 . MD5 por sí mismo proporcionaría una buena solidez criptográfica para el hash de contraseña, pero está diseñado para que sea bastante rápido de calcular en relación con la solidez que proporciona. El esquema crypt() está diseñado para que sea costoso de calcular, para ralentizar los ataques de diccionario. La forma imprimible de los hashes de contraseña MD5 comienza con $1$
.
Este esquema permite a los usuarios tener contraseñas de cualquier longitud y pueden utilizar cualquier carácter compatible con 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 solo 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 vuelve a combinar 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 la frase de contraseña resultante.
El número fijo de iteraciones ha hecho que este sistema pierda el gasto computacional que antes tenía y ahora se favorecen números variables 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 fuertes. [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 $2$
, $2a$
, $2b$
, $2x$
o $2y$
dependiendo de qué variante del algoritmo se utilice:
$2$
- Obsoleto.$2a$
– La clave actual utilizada para identificar este esquema. Desde que en 2011 se descubrió una falla de seguridad importante en una implementación del algoritmo crypt_blowfish que no era de OpenBSD, [16] los hashes indicados por esta cadena son ahora ambiguos y podrían haber sido generados por la implementación defectuosa o por una implementación posterior corregida. 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 a un problema de encapsulamiento. [17] Las versiones anteriores del algoritmo tienen un problema con las contraseñas largas. Por diseño, las contraseñas largas se truncan a los 72 caracteres, pero existe un problema de encapsulamiento de bytes enteros con ciertas longitudes de contraseña que resultan en hashes débiles. [18]$2x$
– Se agregó una bandera después del descubrimiento del error crypt_blowfish . Los hashes antiguos pueden renombrarse para indicar que fueron generados con el algoritmo dañado. 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 usar sin ambigüedades el nuevo algoritmo corregido. En una implementación anterior que tenga el error, simplemente no funcionará. En una implementación más nueva y corregida, producirá el mismo resultado que usar .$2y$
$2b$
Blowfish es conocido entre los cifrados de bloques por su costosa fase de configuración de claves. Comienza con subclaves en un estado estándar, luego utiliza este estado para realizar un cifrado de bloques utilizando parte de la clave y utiliza el resultado de ese cifrado (en realidad, un hash) para reemplazar algunas de las subclaves. Luego utiliza este estado modificado para cifrar otra parte de la clave y utiliza el resultado para reemplazar más subclaves. Procede de esta manera, utilizando un estado modificado progresivamente para realizar un hash de la clave y reemplazar fragmentos de estado, hasta que se hayan establecido 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 NT LAN Manager para proporcionar una compatibilidad más sencilla con las 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 utilizó el $3$
prefijo para esto. No se recomienda su uso, ya que se rompe fácilmente. [1]
El esquema MD5, que se utiliza habitualmente, se ha vuelto más fácil de atacar a medida que aumenta la potencia de los ordenadores. Aunque el sistema basado en Blowfish tiene la opción de añadir rondas y, por tanto, sigue siendo un algoritmo de contraseñas complicado, no utiliza un algoritmo aprobado por el 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 hashes comienza con $5$
(para SHA-256) o $6$
(para SHA-512) dependiendo de la variante SHA que se utilice. Su diseño es similar al cifrado basado en MD5, con algunas diferencias notables: [21]
La especificación y el código de muestra se han publicado en el dominio público; a menudo se lo denomina "SHAcrypt". [24]
$y$
$7$
) y un finalista de PHC. Se utiliza en varias distribuciones Linux como alternativa a los esquemas existentes. [25] Para utilizar 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 del manual de las implementaciones. [27]
BigCrypt es la versión modificada de DES-Crypt que se utiliza en HP-UX, Digital Unix y OSF/1. La principal diferencia entre esta versión y DES es que BigCrypt utiliza todos los caracteres de una contraseña, no sólo los primeros 8, y tiene un hash de longitud variable. [28]
Crypt16 es una modificación menor de DES que permite contraseñas de hasta 16 caracteres. Se utiliza en Ultrix y Tru64. [29]
La biblioteca GNU C (glibc) utilizada por casi todas las distribuciones Linux proporciona una implementación de la función crypt que admite los algoritmos de 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 . [32] Una biblioteca de dominio público crypt_blowfish está disponible para sistemas sin bcrypt. Se ha integrado en glibc en SUSE Linux . [33]
En agosto de 2017, glibc anunció sus planes de eliminar por completo su implementación de cifrado. En respuesta, varias distribuciones de Linux (entre ellas, Fedora y Debian) han cambiado a libxcrypt , una implementación compatible con ABI que además admite nuevos algoritmos, entre ellos bcrypt y yescrypt. [34]
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 UFC-Crypt de glibc . [35]
El sistema nativo de Darwin crypt()
ofrece una funcionalidad limitada, ya que sólo admite DES y BSDi. OS X utiliza unos pocos sistemas para sus propios hashes de contraseñas, que van desde el antiguo netinfo de NeXTStep hasta el sistema de servicios de directorio (ds) más nuevo. [36] [37]