online - almacenar contraseñas hash-base64, o cadena hexadecimal, o algo más?
decode string (10)
Estoy hashing la contraseña usando la clase .NET System.Security.Cryptography. Tiene algunos algoritmos para hash, por ejemplo, MD5, SHA1, SHA256, SHA384, SHA512
El valor hash resultante es una matriz de bytes. ¿Debo convertirlo a una cadena hexadecimal para almacenar, o Convert.ToBase64String (), o a algo más? (Estoy favoreciendo a Base64 ya que es más corto que Hex).
A propósito, con tantos algoritmos hashing para elegir, elegí al azar SHA384, pero ¿hay uno que sea "mejor" o más adecuado para la tarea?
Comentarios por favor.
Actualización después de leer los primeros ocho comentarios:
Siguiendo las respuestas y las lecturas posteriores que he hecho, parece que MD5, SHA1 son más o menos equivalentes (con SHA1 un poco más seguro). SHA256, 384, 512 proporcionan incluso una mejor seguridad en orden creciente.
Como no necesitaré fort-knox (esto es para un sistema corporativo interno sin URL, navegadores, internets, intranets o extranets a la vista), omitiré el negocio de "salazón" - pensé que si alguien puede robar el tabla de contraseñas, también pueden robar los datos reales en otras tablas.
Pero mantendré el concepto de "sal" para futuras referencias; no estoy seguro de si la sal debe ser adjuntada (al final) o anexada (en el frente) de la contraseña antes del hash, y ¿haría una diferencia? También estaba pensando en usar los primeros caracteres de la contraseña como sal, para evitar un campo adicional para almacenarla, pero supongo que no es lo suficientemente larga, y la sal debería ser lo suficientemente larga.
El consenso dice que la conversión de base64 es una opción razonable para el almacenamiento y la comparación. Me resta averiguar cuál es la longitud máxima de columna de la base de datos que necesitaré para almacenar hash, dada una longitud máxima de contraseña de 15 caracteres. Quizás Varchar (64)?
Gracias a todos por su contribución.
Base64 es más corto que el hexadecimal, con la ligera desventaja de que debe tener cuidado si alguna vez termina en una URL (debido a los símbolos involucrados). Depende de dónde lo estés usando, básicamente. Hex normalmente tiene la ventaja de que es más fácil "leer" a simple vista, pero como esto será esencialmente información sin sentido, eso no es relevante para un hash. (Siempre puede usar un decodificador base64 en línea si es necesario).
Todo esto es asumiendo que quieres una cadena, por supuesto. Las cadenas son agradables y fáciles de almacenar, transmitir, etc. en comparación con las matrices de bytes opacos.
La respuesta depende del contexto y la restricción que pueda tener. Base64 será más compacto pero un poco más costoso de generar en comparación con la codificación hexadecimal.
Debe agregar una sal a sus datos hash. Es un valor aleatorio puesto delante de la contraseña que hash. A continuación, almacene el valor de sal con el valor hash para que pueda regenerar el valor hash.
El papel de esta sal es evitar construir dictionnary mapeando contraseñas comunes a valores hash. Si alguien puede obtener una copia de su archivo de contraseña hash, podría usar este diccionario para encontrar la contraseña de texto claro. Con una sal de 16 bits tiene 65536 valores de hash diferentes para la misma contraseña de texto claro. El ataque dictionnary se vuelve menos eficiente. Mejor escoja una sal de 64 bits, porque no agregará mucho más esfuerzo para comprimir y verificar las contraseñas.
También debe agregar una secuencia de bytes mágicos (secuencia de bytes constantes) con el valor hash. El atacante tendrá que conocer esta secuencia de bytes mágicos para poder generar los valores hash apropiados. Así que no irá muy lejos si lo único que tiene es el archivo de contraseñas hash. Tendrá que obtener la palabra mágica que muy probablemente se incluirá en tu código en alguna parte.
En cuanto a los algoritmos para usar SHA1 es muy bueno y SHA256 sería cinturón y correas. MD5 es mucho menos costoso de computar y satisfaría la mayoría de los casos de uso. Considere esto si su recurso de procesamiento y almacenamiento puede ser un factor limitante.
Nota interesante que he visto antes, si tienes una matriz de bytes y tu base64 la codifica, algunos de los caracteres resultantes pueden causar problemas en las URL; sin embargo, si la base 64 la codifica por segunda vez, esos personajes ofensivos tienden a ser eliminado, y la cadena base64 se puede utilizar en urls. Puede comparar usando la secuencia de doble codificación.
En los algoritmos hash, sha384 es probablemente un algoritmo suficiente, ¡pero asegúrese de saltear su hash!
Para las contraseñas, creo que cualquiera de ellas sirve perfectamente. El SHA 512 creará una clave más alta, reduciendo las posibilidades de una "colisión", sabiendo que las diferentes contraseñas pueden tener el mismo hash, pero la posibilidad es muy baja.
También puedes convertirlo a bytes simples. Por lo tanto, un valor hash MD5 de 128 bits daría como resultado una cadena de 128 bit / 8 bit / byte = 16 byte long.
Usaría Base64 .... oh y no olvides saltear tu hash si es una contraseña. Hashing :) Y cualquier cosa menor que SHA384 es un riesgo de seguridad si hablamos de contraseña, porque es muy fácil obtener tablas rainbow de calidad para SHA1 y otros algos de hashing comunes.
Incluso si su solución no es fort-knox, debe ser diligente e implementar la salazón. Debido a que muchas personas reutilizan sus contraseñas en otro lugar, y el robo provocará daños adicionales fuera de su organización, si los atacantes eligen utilizar la base de datos de contraseñas crackeadas para otros fines.
Salting hace que los ataques de diccionario sean más caros. Al decidir qué tamaño de sal usar, puedes afinar tus posibilidades. Aquí está la cita de la "Criptografía Aplicada" de Bruce Schneier:
"La sal no es una panacea: aumentar la cantidad de sal no resolverá todo. La sal solo protege contra ataques generales de diccionario en un archivo de contraseñas, no un ataque concertado contra una sola contraseña. Protege a las personas que tienen la misma contraseña en múltiples máquinas, pero no hace que las contraseñas mal escogidas sean mejores ".
Aquí hay una muestra en C #. No es tan difícil. Y puede elegir qué tamaño de sal y función hash usar. Descargo de responsabilidad: use algo como bcrypt si realmente le importa la integridad de la contraseña.
using System;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
public class PassHash {
private static readonly RandomNumberGenerator rng = RandomNumberGenerator.Create();
public static readonly int DefaultSaltSize = 8; // 64-bit salt
public readonly byte[] Salt;
public readonly byte[] Passhash;
internal PassHash(byte[] salt, byte[] passhash) {
Salt = salt;
Passhash = passhash;
}
public override String ToString() {
return String.Format("{{''salt'': ''{0}'', ''passhash'': ''{1}''}}",
Convert.ToBase64String(Salt),
Convert.ToBase64String(Passhash));
}
public static PassHash Encode<HA>(String password) where HA : HashAlgorithm {
return Encode<HA>(password, DefaultSaltSize);
}
public static PassHash Encode<HA>(String password, int saltSize) where HA : HashAlgorithm {
return Encode<HA>(password, GenerateSalt(saltSize));
}
private static PassHash Encode<HA>(string password, byte[] salt) where HA : HashAlgorithm {
BindingFlags publicStatic = BindingFlags.Public | BindingFlags.Static;
MethodInfo hasher_factory = typeof (HA).GetMethod("Create", publicStatic, Type.DefaultBinder, Type.EmptyTypes, null);
using (HashAlgorithm hasher = (HashAlgorithm) hasher_factory.Invoke(null, null))
{
using (MemoryStream hashInput = new MemoryStream())
{
hashInput.Write(salt, 0, salt.Length);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
hashInput.Write(passwordBytes, 0, passwordBytes.Length);
hashInput.Seek(0, SeekOrigin.Begin);
byte[] passhash = hasher.ComputeHash(hashInput);
return new PassHash(salt, passhash);
}
}
}
private static byte[] GenerateSalt(int saltSize) {
// This generates salt.
// Rephrasing Schneier:
// "salt" is a random string of bytes that is
// combined with password bytes before being
// operated by the one-way function.
byte[] salt = new byte[saltSize];
rng.GetBytes(salt);
return salt;
}
public static bool Verify<HA>(string password, byte[] salt, byte[] passhash) where HA : HashAlgorithm {
// OMG: I don''t know how to compare byte arrays in C#.
return Encode<HA>(password, salt).ToString() == new PassHash(salt, passhash).ToString();
}
}
Uso:
El nuevo usuario envía sus credenciales.
PassHash ph = PassHash.Encode<SHA384>(new_user_password);
Almacene ph.Salt
& ph.Passhash
algún lugar ... Más adelante, cuando el usuario ph.Passhash
sesión nuevamente, busque el registro del usuario que tiene salt & passhash, y luego haga esto:
PassHash.Verify<SHA384>(user_login_password, user_rec.salt, user_rec.passhash)
}
Para responder a la segunda parte de su pregunta, SHA384 probablemente sea excesivo. SHA1 serviría para sus propósitos, pero hay un exploit teórico para esto (relacionado con la posibilidad de crear un documento manipulado que tiene el mismo valor, nada de lo que deba preocuparse).
Y en caso de que no lo esté haciendo, asegúrese de saltear sus contraseñas antes de mezclarlas para protegerlas de los ataques de hackers más comunes (identificar contraseñas similares, tablas de arcoíris, etc.). Lee este artículo para más información.
Para el registro, me gusta Base64 ya que .NET tiene rutinas incorporadas para convertirlo a / desde él.
¿Qué hay de usar la notación Base36 o Base62 que contiene solo alfabetos y dígitos (puede transmitir de forma segura en URLS). Simplemente intenté lo siguiente en Java
BigInteger hexNumber = new BigInteger(hexString, 16);
// Convert it to Base 36 yields smaller string than hexString
hexNumber.toString(36);
Convierte el Número Base36 a Hexagonal
BigInteger b36String= new BigInteger(b36String, 36);
// Convert it to Base 16 yields original hex string
hexNumber.toString(16);
El valor BigInteger Maximum radix de BTW Java es 36.
Para Base62 Podemos tener una tabla de búsqueda con los 62 caracteres (26 más bajos, 26 superiores y 10 dígitos), dividir matemáticamente el número hexadecimal entre 64 y seguir agregando el carácter recuperado de la tabla de búsqueda utilizando el resto.
Sugerencias son bienvenidas.
A propósito, con tantos algoritmos hashing para elegir, elegí al azar SHA384, pero ¿hay uno que sea "mejor" o más adecuado para la tarea?
En general, evitaría MD5 o SHA-0 o SHA-1. Pero SHA-384 debería ser suficiente para sus necesidades en este momento. Vea esta pregunta para una discusión sobre algoritmos.