Cómo proteger las contraseñas de los usuarios

Publicado en Java, Seguridad el 22 de November, 2009 por Fran.

Voy a recuperar otro de mis documentos perdidos en Google Docs (el anterior fue el de las autotools), esta vez se trata de un pequeño manual sobre cómo proteger las contraseñas de lo usuarios de cualquier aplicación que se programe, el manual es una traducción al español del artículo How to encrypt user passwords de Daniel Fernandez.

1. Visión general

Casi todas las aplicaciones web modernas necesitan, de un modo u otro, cifrar las contraseñas de sus usuarios. Se podría decir que, desde el momento en que la aplicación tiene usuarios, y los usuarios se identifican usando una contraseña, esas contraseñas se deben guardar usando algún tipo de cifrado.

Hay muchas razones básicas para esto: las bases de datos pueden estar comprometidas, y por tanto también las comunicaciones. Pero la razón más importante es que se tiene que pensar que las contraseñas de los usuarios son datos personales. Sus contraseñas son sus claves para su privacidad, por tanto son personales, y nadie tiene el derecho de conocerlas. Y se debe cumplir esto si se quiere ganar la confianza de los usuarios.

2. El algoritmo

Ahora que se quieren cifrar las contraseñas, se debe saber cómo. Aquí entra la primera regla:

I. Cifrar las contraseñas usando técnicas de un sentido, es decir, funciones resumen (o hash).

Esto se debe a que, excepto para algunos escenarios específicos, no hay ninguna razón para que una contraseña sea descifrada. Si se cifran las contraseñas usando cifrados basados en claves (una técnica de dos sentidos) y un atacante consigue saber la clave de cifrado, se revelarán todas las contraseñas. Si no se tiene una clave para el cifrado, el riesgo desaparece, y el atacante tendrá que confiar en el uso de técnicas de fuerza bruta o similares.

Pero, ¿qué ocurre si un usuario pierde su contraseña? ¿No podrá recuperarla? La respuesta es NO. No sólo no se puede recuperar, si no que ni siquiera existe una manera de leer/conocer/ver las contraseñas de los usuarios, no importa si se es el administrador del sistema. Si un usuario pierde su contraseña, sólo se podrá reiniciarla a un nuevo valor y enviárselo por algún canal seguro al usuario, y pidiéndole que la cambie lo más pronto que pueda.

Ahora que está claro que las funciones resumen son necesarias en las contraseñas, ¿cuál algoritmo se debería usar? Hay bastantes, y dependen de las necesidades de cada uno. Los más usados son:

  • Algoritmo MD5.
  • Familia SHA: SHA-1 y la variantes de SHA-2 (SHA-224, SHA-256, SHA-384 y SHA-512)

En la mayoría de los casos, MD5 o SHA-1 serán elecciones adecuadas para los resúmenes de contraseñas, aunque aplicar estos algoritmos no será suficiente, como se verá a continuación.

Cuando se dice que se deberían usar funciones resumen para los cifrados de las contraseñas, y dado que estas funciones resumen son técnicas de un sentido, la siguiente pregunta es: «Si no se pueden descifrar las contraseñas, ¿cómo se comprueba que un usuario tiene una contraseña correcta?»

Hay una respuesta muy simple a esta cuestión, que será la segunda regla:

II. La entrada y las contraseñas guardadas se comparan usando los resultados de las funciones resumen, no usando cadenas sin cifrar.

Lo que significa que, una vez los usuarios han introducido sus contraseñas en la identificación, se realizará la función resumen sobre la contraseña introducida con el mismo algoritmo que se ha usado con las contraseñas guardadas, y se compararán ambos resultados. Como estas funciones garantizan que dos entradas iguales obtienen el mismo resultado (lo que no es cierto en el sentido contrario), si los resultados coinciden se considerará que la contraseña introducida por el usuario es correcta.

3. Mejorando la seguridad de los resúmenes

Todos los algoritmos de funciones resumen mencionados anteriormente comparten una característica: son públicos. Son algoritmos muy conocidos y ampliamente implementados, por lo que cualquiera puede usarlos.

Si cualquiera puede usar el mismo algoritmo que la aplicación, y por alguna razón los atacantes pueden ver la base de datos de los resúmenes de las contraseñas, ¿cómo se puede estar seguro que los atacantes no serán capaces de conseguir alguna contraseña de los usuarios probando todas las posibilidades hasta que encuentren algún resumen que coincida con uno de los guardados?

La respuesta es que no se puede. Pero se puede hacer que todo eso sea una tarea enorme y con un gran tiempo de realización, de modo que no resulte útil, a menos que puedan esperar toda la eternidad. Para lograr esto, se van a explicar dos conceptos: «salt» y el contador de iteración.

3.1. «Salt»

«Salt» es una secuencia de bytes que se añaden a la contraseña antes de conseguir su resumen. Esto hace que estos resúmenes sean diferentes a los que deberían ser usando sólo la contraseña, y como resultado se consigue protección contra los ataques de diccionario. Se pueden seguir dos estrategias para usar «salt»:

  • Usar un «salt» fijo, una secuencia de bytes que se utilizarán para resumir cualquier contraseña. Se puede guardar oculto el «salt» y considerarlo un valor añadido de seguridad, pero esto puede hacer que el sistema se vuelva más vulnerable a ataques de cumpleaños y, en general, a ataques dirigidos contra la base de datos de contraseñas.
  • Usar un «salt» variable, que normalmente es una opción segura (especialmente si es aleatorio). Este se genera independientemente para cada contraseña a resumir, y permite que cada contraseña guardada esté desacoplada de las demás, creando una gran protección general y un alta seguridad contra ataques dirigidos contra la base de datos de contraseñas.

En la práctica, un «salt» aleatorio (o variable) es la mejor idea porque, aunque al generarse aleatoriamente tendrá que guardarse junto al resumen de la contraseña (por lo que cualquiera podrá recuperarlo) y esto hará que sea trivial para un atacante conocerlo, permitirá que cada contraseña de usuario sea independiente del resto, por lo que tendrán que atacarse una por una.

Se tiene que pensar que, si se usa un «salt» fijo y el atacante consigue saberlo, la seguridad de toda la base de datos de contraseñas será nula. Y hay muchos métodos para que el atacante pueda conocer este «salt», como aplicar fuerza bruta en sus contraseñas o en contraseñas que tenga de algún usuario válido. En un escenario de un «salt» fijo, una contraseña débil podría hacer que el sistema de contraseñas fuera débil.

Sin embargo, si se quiere mantener una parte del «salt» en secreto, se puede usar una técnica que mezcle las otras dos, usando un «salt» compuesto de una parte fija y otra aleatoria, guardando los bytes aleatorios junto al resumen de la contraseña.

El tamaño mínimo de un «salt» es de 8 bytes. Si se usa una opción mixta, al menos 8 bytes deberían ser aleatorios.

Así, se puede definir la tercera regla:

III. Se debe usar un «salt» que contenga al menos 8 bytes aleatorios, y añadir esos bytes junto al resumen de la contraseña.

3.2. El contador de iteración

El contador de iteración es el número de veces que la función hash que realiza el resumen se aplica a sus propios resultados.

Esto significa que, una vez se ha seleccionado un «salt» y concatenado con la contraseña, se deberá aplicar la función hash (por ejemplo, MD5), conseguir el resultado, y pasarlo de nuevo como entrada a la misma función, y hacer lo mismo las veces que indique el contador de iteración.

El número mínimo recomendado de iteraciones es 1.000, y esto proporcionará una buena seguridad extra. Hay que pensar que, cuando se crea un único resumen de una contraseña para un nuevo usuario, la diferencia entre aplicar la función hash una vez o 1.000 veces no será un problema, tal vez doscientos milisegundos, pero los atacantes deberán tener que generar una enorme cantidad de intentos de resumen mediante fuerza bruta y, para un atacante, la diferencia entre aplicar la función hash una vez y aplicarla mil veces para cada intento será un gran problema computacional.

Así, se obtiene la cuarta regla:

IV. Iterar la función hash al menos 1.000 veces.

4. Secuencia de traducción de caracteres a secuencia de bytes

Antes de poder guardar correctamente los resúmenes de las contraseñas, hay algo que se debería tener en cuenta, que es la diferencia entre cadenas de caracteres y secuencias de bytes: dos cadenas de caracteres se pueden representar con diferentes secuencias de bytes dependiendo de la codificación empleada (ISO-8859-1, UTF-8, …)

Esto afecta, ya que las contraseñas suelen ser cadenas de caracteres, pero las funciones hash trabajan a nivel de byte.

4.1. Problemas codificando la contraseña introducida

En el supuesto de que un nuevo usuario se identifique con una contraseña que no contiene caracteres ASCII, y que la aplicación de identificación, que se está ejecutándo en Windows 2000 en Europa Occidental, convierta la cadena de caracteres a bytes sin elegir una codificación específica para la operación, e incluso usando la que haya de forma predeterminada, en Windows suele ser ISO-8859-1. De modo que el resumen se realice sobre esa secuencia de bytes y quede almacenado.

Entonces, este usuario vuelve de nuevo y esta vez no visita la aplicación de identificación anterior, en vez de eso usa cualquier otra aplicación con la misma base de datos de contraseñas pero en Linux. El usuario introduce correctamente su contraseña y … ¡no coincide!

¿Por qué? Porque la mayoría de los sistemas Linux, usan UTF-8 como codificación predeterminada, y las contraseñas que ha introducido el usuario en ambos casos se han codificado en diferentes secuencias de bytes, afectando a la correcta concordancia de las contraseñas.

Cómo se puede resolver esto, pues utilizando una codificación fija para las codificaciones de las cadena de caracteres a secuencia de bytes. Así se enuncia la quinta regla:

V. Antes de resumir, realizar una codificación de cadena de caracteres a secuencia de bytes usando una codificación fija, preferiblemente UTF-8.

Si se usa Java, donde las cadenas de caracteres (clases String) se codifican independientemente (aunque usan de fondo UTF-16), no habrá que preocuparse de si la aplicación usa ISO-8859-1 o alguna otra codificación en lugar de UTF-8 para su interfaz de usuario, al igual que no será necesario que la codificación para los resúmenes de las contraseñas coincidan con las de la interfaz de usuario. Sólo será necesario que la codificación sea una fija, y UTF-8 proporciona un buen equilibrio entre tamaño y conjunto de caracteres.

4.2. Problemas codificando en el almacenamiento del resumen

Normalmente se quiere gestionar y almacenar el resumen de contraseñas como una cadena de caracteres, pero la función hash dará como resultado una secuencia de bytes que no necesariamente representará un carácter válido en alguna codificación. Por esto, la secuencia de bytes del resumen no se puede codificar en una cadena de caracteres, habiendo peligro de pérdidas de datos si se realizase.

Aquí es donde entra al rescate la codificación BASE64. Codificando la secuencia de bytes del resumen en BASE64, se asegura que la secuencia de bytes de salida representa una cadena de caracteres US-ASCII válida. Por lo que se podrá codificar una secuencia de bytes en BASE64 en una cadena de caracteres US-ASCII.

VI. Finalmente, se debe aplicar la codificación BASE64 y guardar el resumen como una cadena de caracteres US-ASCII.

5. Resumen de las reglas para el cifrado de contraseñas

La lista completa de reglas a aplicar:

  1. Cifrar las contraseñas usando técnicas de un sentido, es decir, funciones resumen (o hash).
  2. La entrada y las contraseñas guardadas se comparan usando los resultados de las funciones resumen, no usando cadenas sin cifrar.
  3. Se debe usar un «salt» que contenga al menos 8 bytes aleatorios, y añadir esos bytes junto al resumen de la contraseña.
  4. Iterar la función hash al menos 1.000 veces.
  5. Antes de resumir, realizar una codificación de cadena de caracteres a secuencia de bytes usando una codificación fija, preferiblemente UTF-8.
  6. Finalmente, se debe aplicar la codificación BASE64 y guardar el resumen como una cadena de caracteres US-ASCII.

6. Realizándolo en Java

La forma más sencilla de cifrar contraseñas en Java, mediante las técnicas explicadas es usar Jasypt.

Si no se puede usar jasypt, o si por alguna razón se quiere desarrollar alguna característica de cifrado propia, se necesitarán:

  • La clase java.security.MessageDigest para crear resúmenes. Esta clase permite especificar el algoritmo que se va a usar.
  • La clase java.security.SecureRandom para generar «salt» aleatorios de una manera segura, usando algoritmos como SHA1PRNG.
  • El método java.lang.String.getBytes (String charsetName), para obtener una secuencia de bytes desde una cadena de caracteres, especificando una codificación fija (UTF-8).
  • La clase org.apache.commons.codec.binary.Base64, parte de la biblioteca Apache Commons-Codec, para realizar codificaciones BASE64 en la salida de la función hash.

También, se puede utilizar el código fuente para las clases org.jasypt.digest.StandardByteDigester y org.jasypt.digest.StandardStringDigester, disponible en el repositorio de jasypt.

7. Defensa frente a los típicos ataques

7.1. Ataques de fuerza bruta

Realizado sobre: Una única contraseña.

Descripción: El atacante intenta conseguir la contraseña del usuario generando todas las contraseñas posibles, resumiéndolas y probando si coinciden con el resumen de la contraseña del usuario.

Defensa: La iteración de la función hash un número de veces, como 1.000 (mínimo recomendado), el coste de la creación del resumen de la contraseña en el tiempo de identificación del usuario no es significante, pero el coste de un ataque de fuerza bruta por parte de un atacante que genera millones de resúmenes será muy grande. Hay que recordar que una de las mejores maneras de proteger los datos cifrados es haciendo que el coste de romper la seguridad sea más alto de lo que cuesta el esfuerzo.

7.2. Ataques de diccionario

Realizado sobre: Cada contraseña o una base de datos de contraseñas

Descripción: El atacante intenta conseguir la contraseña de un usuario comparando sus resúmenes contra un conjunto de resúmenes de contraseñas más probables, normalmente se generan desde una lista de palabras de un diccionario. Este ataque explota una debilidad actual de las aplicaciones, ya que muchos usuarios usan palabras del diccionario como contraseña.

Defensa: Añadiendo un «salt» aleatorio, la debilidad de las contraseñas basadas en diccionario se reduce, y la posibilidad de de que el resumen aparezca en un conjunto de resúmenes previamente creados por el atacante es mínima.

7.3. Ataques de cumpleaños

Realizado sobre: La base de datos de las contraseñas.

Descripción: Este ataque explota la paradoja del cumpleaños, la cual en resumen consiste en, teniendo un gran conjunto de resúmenes de contraseñas, las posibilidades de generar una contraseña cuyo resumen coincida con al menos uno de los resúmenes en el conjunto es mucho más alto que lo esperado. Y estas posibilidades aumentan bastante cuando el tamaño del conjunto (el número de usuarios) aumenta.

Defensa: Añadiendo un «salt» aleatorio las posibilidades de que un ataque de cumpleaños tenga éxito son mínimas, porque el atacante tendría que atacar cada contraseña por separado, y no el conjunto de contraseñas, para encontrar una coincidencia. Esto es porque se tendría que encontrar una contraseña que crease el mismo resumen que el del atacante usando el mismo «salt» que se usó para el resumen, y que es diferente para cada contraseña haciendo que el ataque pase a ser de fuerza bruta.

Espero que este pequeño manual os sea de utilidad, a mí me sirvió para mi PFC.

18 comentarios

  1. Impresionado me dejas…

    #  Lek 23 de November, 2009

  2. [...] – Interesante artículo sobre la generación de contraseñas, salts y demás monstruos [...]

    #  Gaea – Redes Sociales Verticales | Comunidades Online | Web 2.0 | Agile Development | E-Salud | Software Libre » Blog Archive » Jasypt: Java Simplified Encryption 23 de November, 2009

  3. Lek realmente yo no soy quién te ha dejado impresionado sino Daniel que es el autor del artículo.

    #  Fran 23 de November, 2009

  4. Coño, que interesante. El punto 3.2 jamás lo había contemplado y resulta muy interesante. Lo aplicaré.

    #  Hugo 24 de November, 2009

  5. Hola, soy el autor original del artículo.

    Muchas gracias por el buen trabajo que has hecho traduciéndolo al castellano. Si no te importa, voy a enlazarlo desde la página de recursos de la web del proyecto Jasypt [http://www.jasypt.org] (donde fue publicada la versión en inglés originalmente).

    Saludos,
    Daniel.

    #  Daniel Fernández 25 de November, 2009

  6. Daniel no me importa en absoluto.

    #  Fran 25 de November, 2009

  7. Siento decirte que la aplicación reiterada de la función hash no aporta un incremento de la seguridad del sistema, más bien todo lo contrario (por lo general, la reiteración de la aplicación de la función hash puede tener otras funciones importantes para la seguridad que no has comentado). Me explico:

    La función hash, es, por definición (en términos matemáticos) una función que se aplica sobre un conjunto numerable de cadenas de bits, su imagen es un conjunto finito de cadenas de bits de longitud constante. Es decir, no es una función suprayectiva (o exhaustiva). La aplicación de la función sobre cualquier cadena provoca que la siguiente aplicación se haga sobre un subconjunto del conjunto inicial drásticamente reducido, lo que podría provocar (y suele provocar, aunque depende de la función hash) una reducción aun más drástica de lo que puede ser el conjunto imagen. Esto comporta (la demostración es algo compleja para ponerla en un post, aunque tampoco demasiado, intuitivamente se puede vilumbrar) que aumenta la cantidad de cadenas dentro del conjunto inicial que pueden tener la misma imagen final, ergo, se reduce la seguridad… pues no hace falta ni acertar la contraseña correcta!

    Alguien dice que el punto 3.2 nunca lo había contemplado… y mejor era así, has dado un muy mal consejo.

    La aplicación reiterada de funciones hash (no se llega al extremo de 1000 veces, hasta donde he visto, el máximo de iteraciones que se usan es de 3 o 4) se usa a veces en canales inseguros para poder construir redes oscuras, darknets, tales como GNUNet o Freenet y el objetivo en este caso no es proteger información almacenada, es proteger de espionage a los interlocutores en un intercambio de información (Por muy cifrado que esté el canal, casi siempre es posible ejercer un ataque “man in the middle”, de ahí el uso de la técnica).

    Saludos!

    #  castarco 25 de November, 2009

  8. Olvidaba comentar lo de las 200 veces concatenadas, eso se puede saltear teniendo el código de la función, pues la composición de funciones es susceptible de ser simplificada.

    #  castarco 25 de November, 2009

  9. castarco no estoy muy puesto en el tema por lo que no me puedo posicionar sobre la validez de tu comentario, pero no puedo negar que tu explicación tiene cierta lógica. Como comenté al principio del post, esto no es más que una mera traducción, estaría encantado si Daniel pudiera dar su opinión sobre tu comentario o sobre por qué él cree que sí es bueno realizar las iteraciones.

    ¡Ah! Tu último comentario no lo entiendo, ¿a qué te refieres con lo de las 200 veces concatenadas?

    #  Fran 25 de November, 2009

  10. Castarco, lo que yo he entendido de ejecutar el hash 1000 veces es aplicarlo… sobre la misma cadena inicial. Es decir, no se reduce la seguridad, simplemente se aumenta artificialmente el tiempo de respuesta al realizar ejecuciones “de más”.

    Efectivamente, no aumenta la seguridad, porque el resultado ha de ser las 1000 veces el mismo, sólo aumenta el tiempo de respuesta. Y ese tiempo, que para una petición es prácticamente inapreciable, puede ser determinante para que un bot que esté “probando suerte” tarde meses “de más” en dar con la contraseña. Así lo he entendido yo, vamos…

    #  Lek 25 de November, 2009

  11. Lek pues lo has entendido mal, mira el artículo original que tiene un dibujo muy majo explicándolo.

    #  Fran 25 de November, 2009

  12. Perdon por lo de “las 200 veces concatenadas” xD, me he expresado como un libro cerrado.

    Me refería a que si se aplica reiteradamente una función hash sobre un dato esto no tiene porqué traducirse en tiempos muchos más altos para realizar el cómputo, pues conociendo el código y los fundamentos matemáticos de ésta, se puede “simplificar” el código para que con muchas menos instrucciones realice un cálculo equivalente a la composición reiterada de la función (si el numero de interaciones es conocido y fijo, en todo caso, incluso siendo variable se pueden hacer diversas simplificaciones que ayuden en la reducción de tiempos).

    #  castarco 25 de November, 2009

  13. Hola,

    Quisiera aclarar que la teoría de que la iteración en el hashing reduzca el nivel de seguridad es incorrecta, aunque sí es cierto que causa cierta discusión en muchos foros. Y me explico:

    1. Una función de hash da como resultado una determinada cantidad fija de bytes, por ejemplo, 64. Y los resultados de esa función de hash se supone que están uniformemente distribuidos entre los posibles valores de esos 64 bytes.

    2. El hecho de que el tamaño del resultado sea “x”, significa que tenemos unas probabilidades “y” de que tengamos una colisión entre dos entradas a la función de hash. Y ese “y” depende directamente de “x”: a más tamaño, menos probabilidades (siempre que la función hash esté bien hecha y distribuya uniformemente sus resultados, claro).

    3. Si aplicamos la función hash una segunda vez, el resultado seguirán siendo “x” bytes, exactamente el mismo tamaño, y por tanto la probabilidad de colisión seguirá siendo “y”.

    Esto significa que “a igual número de iteraciones hash”, la probabilidad de colisión es la misma se aplique la función hash 1 ó 100 veces.

    Obviamente, si yo tengo que romper una clave que se iteró 100 veces, y para considerar mis posibilidades de encontrar una colisión considero las posibilidades que tengo de conseguir el mismo hash después de iterar 1, 2, 3, 4, … hasta 100 veces, entonces sí tengo una probabilidad muchísimo mayor, claro está. Porque para una entrada no estoy considerando una salida, sino 100 (una para cada número de iteraciones).

    Pero es que esto último no vale. Porque para “romper” una clave, necesito una entrada que me dé exactamente el mismo resultado hash después de que la aplicación lo haya procesado para compararlo con la clave almacenada. Y si esta aplicación itera la función de hash 100 veces, sólo me valen las colisiones a exactamente 100 iteraciones, no a 1, ni 2, ni 3… ni 99.

    De modo que no, iterar la función hash no disminuye la seguridad ni la aumenta si sólo consideramos las probabilidades de colisión, pero aumenta la seguridad de la aplicación porque aplicar una función hash es computacionalmente complejo, y esto hace que cualquier ataque por fuerza bruta requiera muchísimo (realmente muchísimo) más tiempo de computación para romper una clave iterada 100 veces que una clave iterada sólo una vez.

    Como fundamento de lo que digo, os recomiendo que leáis el estándar PKCS #5 publicado por RSA sobre Password-Based Encryption, en el que, en el apartado donde habla de cómo generar claves de cifrado desde passwords textuales (lo que se hace siempre mediante la aplicación de un hash), explica por qué se aplica una iteración de la función Hash (apartado 4.2 de la versión 2.1 del estándar).

    #  Daniel Fernández 25 de November, 2009

  14. La reducción del conjunto imagen es una cosa clarísima, es un subconjunto del conjunto imagen para la función hash aplicada una sola vez, y aplicada tres veces el conjunto imagen se reduce aun más, por lo que a más iteraciones, más colisiones para el mismo conjunto de partida. El tamaño del hash puede ser el mismo tal y como dices, pero resulta que no todos los hash posibles se daran en la segunda iteración (algunos no serán alcanzables), y en la tercera iteración habrá menos hash alcanzables, y así hasta que nos quede un conjunto mínimo e irreductible en el que la función hash actue de forma biyectiva.

    Por otro lado, la única forma de que no pasara lo que describo sería haciendo que la funcion hash actuara de forma biyectiva si se restringe a su conjunto imagen, es decir, si es un automorfismo sobre su conjunto imagen. El hecho de que sea un automorfismo sobre su conjunto imagen, puede, a su vez, reducir aun más la dificultad de encontrar contraimágenes y reducir consecuentemente la seguridad de la función hash, por lo que la composición iterada no es más segura. Sólo es más segura desde el punto de vista intelectual, pues la simplificación de los cálculos puede resultar muy compleja… aunque solo se tiene que hacer una sola vez, y funcionará para siempre. Que no se haya simplificado nunca la composición de funciones hash no significa que sea imposible.. y una vez hecha no se tiene que volver a hacer nunca más para ningún otro ataque.

    #  castarco 25 de November, 2009

  15. Hola castarco,

    Entiendo tu razonamiento, pero se basa en que la función hash no distribuya uniformemente sus resultados a lo largo de su espacio de posibles salidas, esto es, que no sea inyectiva (lo que se denomina una “función hash perfecta”). Inyectiva quiere decir que aunque su entrada sea del mismo tamaño que su salida (que es el caso que tenemos a partir de la segunda iteración), dará un valor de salida diferente para cada valor de entrada diferente.

    Por supuesto no contamos con el caso en que la entrada es más pequeña que la salida (por ejemplo, una password de 2 caracteres), porque en ese caso claramente la función hash no tiene nada que hacer ya que el espacio de entradas nunca podrá cubrir todo el espacio de salidas posible.

    Aunque obviamente no todas las funciones hash son inyectivas (hay funciones hash para dar y tomar), algunas sí. Y desde luego las criptográficas tienen como una de sus misiones “aspirar” a serlo (aunque teóricamente deberían serlo todas si fuesen, eso, “perfectas”).

    ¡Ajá! dirás, ¡así que reconoces que aunque en teoría lo sean, en la práctica no son inyectivas y que por tanto tengo razón! pues no :-). En la práctica, la “no inyectividad” de estas funciones es absolutamente mínima (y despreciable), y la pérdida de espacio de salidas que provoquemos iterando la función 1000 veces (que, repito, es lo que recomienda RSA para crear claves de cifrado a partir de passwords textuales) compensa más que sobradamente la diferencia en complejidad computacional de aplicar la función esa cantidad de veces.

    Un saludo.

    #  Daniel Fernández 25 de November, 2009

  16. Correcto, Fran, mi cerebro ignoró la parte “a sus propios resultados”…

    #  Lek 25 de November, 2009

  17. Al respecto de todo este tema, he hecho un par de búsquedas rápidas y he visto que varios entornos realizan iteraciones sobre los hashes. Por ejemplo: en el password.inc de Drupal y/o en JBoss Seam.

    Supongo que habrá muchos más, por lo que he leído casi todo el mundo se basa en el estándar PKCS #5 que ha mencionado Daniel.

    #  Fran 25 de November, 2009

  18. Me quedao flipao con los comentarios!

    Que cracks! jajaj

    Un saludo!

    #  Tutor Dk 16 de April, 2010

Escribe un comentario