passwordhasher password net mvc hashpassword example encrypt asp asp.net security encryption hash

asp.net - password - Cifrado de contraseña efectivo



password hash asp.net identity (8)

  1. El objetivo de hashing es almacenar las contraseñas hash en lugar de las de texto plano y no se pueden recuperar (al menos solo con gran dificultad). Las funciones hash están diseñadas para ser unidireccionales, por lo que la recuperación es imposible ; esta es su verdadera esencia.
  2. nchar[n] es probablemente la mejor opción, ya que los algoritmos hash comunes producen resultados de longitud constante ( SHA1 genera un hash de 160 bits). La práctica común es convertir el hash a Base64 para que pueda almacenarse como texto ASCII.
  3. Una sal es solo un método que agrega bits aleatorios en un hash para complicar el proceso de agrietamiento. Cada cantidad extra de sal significa que un cracker de fuerza bruta debe tomar el doble de tiempo (y usar el doble de memoria).

He echado un vistazo a la pregunta de StackOverflow, "Encriptación de contraseña / Capa de base de datos AES o AES de capa de aplicación", y me gustaría tener mis contraseñas de forma efectiva y eficiente en el registro (aplicación web) y luego poder verificar que son corregir al iniciar sesión. Estoy usando VB, pero me siento cómodo usando C #.

Me encantaría utilizar la clase de cifrado de Jeff Atwood que se describe en ".NET Encryption Simplified", ya que es muy fácil de entender. Tiene una clase de hash, pero no tengo idea de cómo "iniciar sesión" y comparar hashes después de haber sido hash. Esta es la demostración de Jeff de sus métodos hash usando su clase de cifrado:

Sub DemoHash() Dim d As New Encryption.Data( _ "{ts ''2004-10-09 08:10:04''}The world is beautiful and needs caring by its children") Dim hash As New Encryption.Hash(Encryption.Hash.Provider.SHA1) Dim hash2 As New Encryption.Hash(Encryption.Hash.Provider.SHA256) Dim hash3 As New Encryption.Hash(Encryption.Hash.Provider.SHA384) Dim hash4 As New Encryption.Hash(Encryption.Hash.Provider.SHA512) Dim hash5 As New Encryption.Hash(Encryption.Hash.Provider.MD5) Dim hash6 As New Encryption.Hash(Encryption.Hash.Provider.CRC32) hash.Calculate(d) hash2.Calculate(d) hash3.Calculate(d) hash4.Calculate(d) hash5.Calculate(d) Console.WriteLine("SHA1: " & hash.Value.Hex) Console.WriteLine("SHA256: " & hash2.Value.Hex) Console.WriteLine("SHA384: " & hash3.Value.Hex) Console.WriteLine("SHA512: " & hash4.Value.Hex) Console.WriteLine("MD5: " & hash5.Value.Hex) Console.WriteLine("CRC32: " & hash6.Calculate(d).Hex) Console.WriteLine() Dim salt As New Encryption.Data("salty!") Console.WriteLine("Salted CRC32: " & hash6.Calculate(d, salt).Hex) Console.WriteLine("Press ENTER to continue...") Console.ReadLine() End Sub

Entonces mis preguntas son:

  1. Puedo encriptar la contraseña (aunque no tengo intención de almacenarla) y codificar una cadena. Si tuviera un usuario llamado ''barry'' con una contraseña de ''fishlegs'', ¿cuál es la mejor manera de almacenar su contraseña y recuperarla?

  2. En SQL Server; ¿Es binario o nvarchar la mejor opción para el almacenamiento del hash?

  3. Basado en ''barry'' y su contraseña, ¿qué es efectivamente el almacenamiento de hash? ¿Es una encriptación de ''fishlegs'' añadida a una sal?

¡La criptografía es difícil!

Gracias a cualquiera que pueda ayudar ...


ASP.NET incluye un proveedor de membresía basado en SQL que te permite crear, almacenar y verificar contraseñas cifradas o hash , sin que tengas que hacer nada, como dice Eric Lippert:

permítame darle toda mi precaución estándar sobre cómo implementar sus propios algoritmos criptográficos y sistemas de seguridad: no.


Creo que el proceso es el siguiente: generar una sal aleatoria, por lo que ''fishlegs'' + ''r4nd0mS4lt'' para obtener ''fishlegsr4nd0mS4lt''. Luego hash que, digamos con MD5 (aunque es posible que desees usar SHA256 para ser más seguro) para obtener: 593d5518d759b4860188158ef4c71f28. Almacene eso y la sal generada al azar. Cuando el usuario inicie sesión, agregue la sal aleatoria y luego verifique si su contraseña introducida con la sal coincide con el hash en la base de datos.


De acuerdo, un par de cosas sobre la clase de Jeff antes que nada. SHA1 y MD5 están en desuso ahora. CRC32 no es adecuado para contraseñas. En segundo lugar, deberías salar cada picadillo, preferiblemente con un valor de sal diferente. En general, usted elige un bloque de datos criptográficamente aleatorio para esto, pero con un impulso puede usar el nombre de usuario. Para sal, prefijo, o sufijo el valor de sal en algún lugar del proceso. Tiendo a hash la contraseña, hash la sal, combino los dos, luego hash nuevamente. Pero puedes intercambiar cosas, realmente no importa tanto, siempre y cuando seas consistente.

Entonces, en lugar de confundir más las cosas con la clase de Jeff, hagamos esto de la manera clásica.

Primero en la generación de sal aleatoria.

public static byte[] GetRandomSalt() { int minSaltSize = 16; int maxSaltSize = 32; Random random = new Random(); int saltSize = random.Next(minSaltSize, maxSaltSize); saltBytes = new byte[saltSize]; RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); rng.GetNonZeroBytes(saltBytes); return saltBytes; }

Luego hashing

public static byte[] ComputeHash(string plainText) { byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText); HashAlgorithm hash = new SHA256Managed(); return hash.ComputeHash(plainTextWithSaltBytes); }

Así que esto calculará un hash SHA256 y lo devolverá como una matriz de bytes.

Si estuvieras salando, harías algo como lo siguiente

byte[] passwordHash = ComputeHash(password); byte[] salt = GetRandomSalt(); byte[] saltHash = ComputeHash(salt); byte[] hashWithSaltBytes = new byte[hashBytes.Length + saltBytes.Length]; for (int i=0; i < hashBytes.Length; i++) hashWithSaltBytes[i] = hashBytes[i]; for (int i=0; i < saltBytes.Length; i++) hashWithSaltBytes[hashBytes.Length + i] = saltBytes[i];

Y luego, si está aburrido, descárguelo de nuevo, o váyase tal como está.

Para convertir una matriz de bytes en una cadena, si no desea almacenar bytes, puede usar

string hashValue = Convert.ToBase64String(hashWithSaltBytes);

Las comparaciones de cadenas son más fáciles que las comparaciones de bytes, donde tiene que iterar sobre cada arreglo, depende de usted. Solo recuerde que si usa sales aleatorias, debe almacenarlas junto a la contraseña.


Hmm, creo que te faltan algunos conceptos básicos relacionados con el funcionamiento de hash. Déjame tratar de explicar brevemente. Voy a comenzar de manera simple y detallada sobre mi respuesta después, así que por favor, lea todo, la información al principio no será segura.

Lo que quiere usar para almacenar una contraseña es una función conocida como "hash unidireccional". Lo que esto significa es que, para cualquier entrada que alimente la función, la misma entrada siempre dará el mismo resultado. Sin embargo, no existe un proceso matemático que le permita tomar esa cadena de resultados y descubrir cuál fue la entrada original.

Tomemos MD5 como un ejemplo de una función hash. Si ejecuto MD5 en la cadena "contraseña", siempre obtendré el resultado "5f4dcc3b5aa765d61d8327deb882cf99". Sin embargo, si le diera simplemente a alguien la cadena resultante ("5f4d ..."), es imposible que aplique algún proceso matemático para "revertir" la función y descubrir que proviene de "contraseña".

Lo que esto significa es que cuando un usuario establece su contraseña por primera vez, le aplica una función de hashing y almacena el resultado. Entonces, en lugar de almacenar "contraseña", almacena "5f4dcc3b5aa765d61d8327deb882cf99". Luego, cuando ese usuario intente iniciar sesión, tome lo que haya escrito en el cuadro de contraseña en el formulario de inicio de sesión y aplique la misma función de hash. Si obtienes el mismo resultado que el que está almacenado en la base de datos, deben haber ingresado la misma contraseña que eligieron originalmente, aunque no tienes idea de cuál fue realmente esa contraseña original.

Ahora, a pesar de que es imposible "revertir" una función hash, el hecho de que la misma entrada proporcione siempre la misma salida significa que alguien simplemente puede construir una gran base de datos de pares de entrada / salida, y usar eso para revertir efectivamente hashes. Esto se llama una "mesa de arcoiris". Hay muchos de estos disponibles en Internet, por lo que no es seguro usar hashing simple, en caso de que su base de datos alguna vez se vea comprometida. Es decir, aunque matemáticamente es imposible tomar "5f4dcc3b5aa765d61d8327deb882cf99" y descubrir que proviene de ejecutar MD5 en "contraseña", es muy fácil determinarlo en la práctica. Todo lo que tiene que hacer es ejecutar cada palabra en un diccionario a través de MD5 y almacenar los resultados, y puede revertir fácilmente las contraseñas simples.

Aquí es donde entra en juego la "salazón". Si genera una cadena aleatoria de "sal" para cada usuario y la adjunta a su contraseña, arruina efectivamente las tablas del arcoíris. Por ejemplo, supongamos que el mismo usuario anterior registra con su contraseña como "contraseña". Generamos un sal aleatorio de 8 caracteres para adjuntar a su contraseña antes de hash. Digamos que es "A4BR82QX". Ahora, en lugar de hash "contraseña", hemos hash "A4BR82QXpassword". Esto da el resultado "87a4ba071c8bcb5efe457e6c4e6c4490", así que almacenamos eso en la base de datos, junto con la cadena de sal. Luego, cuando este usuario intenta iniciar sesión, en lugar de realizar directamente el hashing y comparar la contraseña que ingresaron en el formulario de inicio de sesión, tomamos lo que ingresaron, colocamos "A4BR82QX" delante de él nuevamente y lo calificamos. Al igual que antes, si coincide con el hash almacenado, sabemos que ingresaron la contraseña correcta.

Efectivamente, lo que ha hecho aquí es hacerlo de modo que las tablas rainbow pregeneradas sean inútiles para tratar de descifrar las contraseñas en su base de datos. Como la sal es aleatoria y cada usuario tiene una diferente (generalmente), el atacante tendrá que volver a generar sus tablas de arcoiris para cada usuario individual . Esto es mucho mas dificil

Sin embargo, hay un problema más, y es que generar hashes MD5 es rápido . A pesar de que la salazón de este tipo requiere que vuelvan a generar tablas de arcoíris, debido a la rapidez con que MD5 es capaz de crear rápidamente algunas tablas de arco iris decentemente completas. Entonces, si solo quieren crear una cuenta de alto valor en su sitio, no es realmente un gran problema para ellos dedicar algo de tiempo a generar tablas arcoíris para tratar de revertir esa contraseña. Si la contraseña original de la cuenta de alto valor no era lo suficientemente segura por sí misma, aún se encontrará muy rápidamente, incluso con salazón.

Entonces, el siguiente paso es encontrar una función hash lenta y usarla en lugar de una rápida como MD5. Hacer que tu sitio tome unos segundos extra para verificar el inicio de sesión no es un gran problema. Pero cuando alguien intenta generar tablas arcoíris para descifrar una contraseña, hacer que cada entrada tarde varios segundos es un asesino absoluto. He escrito suficiente aquí, así que terminaré al vincular este artículo, que contiene muchos detalles sobre cómo elegir una función de hash lenta y buena: basta con las tablas del arco iris: lo que debe saber sobre los esquemas de contraseña segura .

Esa fue una gran respuesta, si algo de eso no está claro, por favor, hágamelo saber en un comentario y lo editaré para elaborarlo.


Lo que esencialmente quieres hacer es:

A) En la creación de la cuenta, obtienes un nombre de usuario y contraseña del usuario, los hash junto con tu propia sal y almacenas la cadena resultante en tu base de datos como:

Dim sUserName = "barry" Dim sPassWord = "fishlegs" Dim mySalt = "A deliciously salty string! fillED WIth all KindS of Junkk(&^&*(£" Dim d As New Encryption.Data(mySalt + sUserName + sPassWord) Dim hash As New Encryption.Hash(Encryption.Hash.Provider.SHA256) hash.Calculate(d) Dim sTheSaltedHashedUnPassCombination = hash.Value.Hex; SavenewPasswordHashToDatabase(sTheSaltedHashedUnPassCombination)

Nunca almacena sPassWord.

B) Cuando el usuario inicia sesión, realiza exactamente la misma operación en el nombre de usuario y la contraseña proporcionados, luego compara el hash resultante con el valor previamente almacenado en la base de datos para que pueda usar:

Dim sThePreviouslyCreatedHashFromTheDatabase = GetHashForUserFromDatabase(usernameProvided) Dim mySalt = "A deliciously salty string! fillED WIth all KindS of Junkk(&^&*(£" Dim d As New Encryption.Data(mySalt + usernameProvided+ passwordProvided) Dim hash As New Encryption.Hash(Encryption.Hash.Provider.SHA256) hash.Calculate(d) Dim sTheSaltedHashedUnPassCombination = hash.Value.Hex; if (sThePreviouslyCreatedHashFromTheDatabase.Equals(sTheSaltedHashedUnPassCombination)) ''UN & Password Valid! else ''UN & PW Invalid! end

(Perdón cualquier error, VB no es mi lenguaje)

Para responder a sus preguntas dadas:

1) Ver arriba. Nunca almacene la contraseña directamente, almacene el valor hash

2) use un carácter (X), el número de caracteres devueltos del hash es constante para que pueda darle un tamaño de almacenamiento conocido.

3) Lo que está almacenando efectivamente es la contraseña para el usuario salado con su nombre de usuario y también salado con la constante en su servidor. Esta será una cadena de longitud fija y no puede volver a cambiar esta cadena en el nombre de usuario y la contraseña por separado.


No confundas las contraseñas de encriptación con hash; con una buena función de hash criptográfica, no debería poder invertir el proceso de hash para recuperar la cadena original.

La respuesta de Chad anterior es una excelente explicación punto por punto de los conceptos involucrados.

Este tema ha sido asesinado a través de Internet; no solo en ; en serio: una simple búsqueda web debería encontrarle una guía bastante completa.

Gracias, Jeff, por difundir otro montón de desinformación. Supongo que veremos una gran cantidad de preguntas equivocadas sobre hash, encriptación, etc. durante la próxima semana, por no mencionar la mierda que surgirá sobre la aritmética de coma flotante.


  • Tome la contraseña de usuario " ¡Secreto! ".
  • Genera una sal aleatoria de unos pocos bytes " s4L75a1T ".
  • Concat a " s4L75a1TSecret! ".
  • Calcula el hash " b9797a5b683804eb195b6ba2a5e368ae74394cd3 " (esto es SHA-1 en hexadecimal)
  • Guarde el hash y la sal (ambos como cadena hexagonal, cadena Base64 o lo que quiera) en la base de datos junto con el nombre de usuario y la otra información del usuario (en el ejemplo, la sal es simplemente una cadena simple y el hash está codificado en Base64 )

FirstName LastName Salt Hash ----------------------------------------------------------------- John Doe s4L75a1T uXl6W2g4BOsZW2uipeNornQ5TNM=

Si desea verificar la contraseña del usuario, simplemente tome el nombre de usuario, busque la sal, haga lo anterior nuevamente y vea si el hash calculado y el de la base de datos coinciden. No hay forma (conocida) de recuperar (fácilmente) la contraseña.