Encontramos el arreglo a esta traba, o por lo menos eso deseamos. Si tienes preguntas puedes dejarlo en el apartado de preguntas y sin pensarlo
Solución:
Rfc2898DeriveBytes
implementa PBKDF2: una función que convierte una contraseña (con una sal) en una secuencia de bytes de longitud arbitraria. PBKDF2 se utiliza a menudo para el hash de contraseñas (es decir, para calcular y almacenar un valor que sea suficiente para verificar una contraseña) porque tiene las características necesarias para las funciones de hash de contraseña: sal y lentitud configurable.
Estas características son necesarias porque las contraseñas son débiles: encajan en el cerebro humano. Como tales, son vulnerables a una búsqueda exhaustiva: es factible, en general, enumerar la mayoría de las contraseñas que los usuarios humanos obtendrán y recordarán. El ataque asume que el atacante obtuvo una copia de la sal y la contraseña hash, y luego “probará las contraseñas” en su propia máquina. Eso se llama ataque de diccionario fuera de línea.
En su caso, tiene un tercer elemento: un validación key. Es un key, es decir, supuestamente secreto. Si el atacante podría tomar las sales y las contraseñas hash, pero no la validación key, entonces no puede realizar el ataque de diccionario en sus propias máquinas; bajo estas condiciones (la validación key permanece en secreto, y el algoritmo de validación es robusto (HMAC / SHA-512 está bien para eso), la lentitud configurable de PBKDF2 no es necesaria. Este tipo de validación con un secreto key a veces se le llama “pimienta”.
Tenga en cuenta, sin embargo, que cuando asumimos que el atacante podría obtener una copia de las contraseñas hash, entonces se vuelve una cuestión de delicadeza suponer que el key permaneció inmaculado por sus viles miradas. Depende del contexto. La mayoría de los ataques de inyección SQL podrán leer parte de toda la base de datos, pero no el resto de los archivos de la máquina. Sin embargo, su servidor debe poder arrancar y arrancar de alguna manera sin intervención humana, por lo que la validación key está en algún lugar del disco. Un atacante que robe todo el disco (o una cinta de respaldo …) obtendrá la validación key también, momento en el que vuelve a la necesidad de una lentitud configurable.
En términos generales, recomendaría PBKDF2 (también conocido como Rfc2898DeriveBytes
en .NET) sobre una construcción personalizada, aunque debo decir que parece que usas HMAC correctamente (las construcciones caseras rara vez alcanzan ese nivel de corrección). Si insiste en tener una “validación key”(y está listo para asumir la sobrecarga procesal de key gestión, por ejemplo, copias de seguridad especiales para eso key), luego sugiero usar PBKDF2 y luego aplicando HMAC en la salida PBKDF2.
Consulte esta respuesta para obtener una discusión detallada sobre el hash de contraseñas.
Estoy respondiendo específicamente a la EDICIÓN de lecciones aprendidas en la pregunta original.
Se llama a Rfc2898DeriveBytes con 1000 iteraciones. utilizando una salida de 1024 bytes. Afortunadamente, el tamaño de la contraseña en Db fue diseñado 2k. La muestra promedio en las pruebas en estaciones de trabajo fue de alrededor de 300 ms
Resumen rápido: si le gusta su carga actual de CPU y Rfc2898DeriveBytes, cambie de 1000 iteraciones y una salida de 1024 bytes a 52000 iteraciones y una salida de 20 bytes (20 bytes es la salida nativa de SHA-1, que es lo que .NET 4.5 Rfc2898DeriveBytes está basado en).
La explicación del por qué, incluidas las referencias:
Para evitar que los atacantes tengan una ventaja sobre usted, NO use un PBKDF2 / RFC2898 / PKCS # 5 (o un HMAC, que se usa internamente en PBKDF2 et al.) Con un tamaño de salida mayor que la salida nativa de la función hash utilizada. Dado que la implementación de .NET (incluso a partir de 4.5) codifica SHA-1, debe usar un máximo de 160 bits de salida, ¡no 8192 bits de salida!
La razón de esto es que, como nos referimos a la especificación RFC2898, si el tamaño de salida (dkLen, es decir, longitud de clave derivada) es mayor que el tamaño de salida de hash nativo (hLen, es decir, longitud de hash). En la página 9, vemos
Paso 2: “Sea l el número de bloques de octetos hLen en el key, redondeando”
Paso 3: “T_1 = F (P, S, c, 1), T_2 = F (P, S, c, 2), … T_l = F (P, S, c, l),”
Y en la página 10: Paso 4: “DK = T_1 || T_2 || … || T_l<0..r-1>“donde DK es el derivado key (Salida PBKDF2) y || es el operador de concatenación.
Por lo tanto, podemos ver que para su 8192 bit key y HMAC-SHA-1 de .NET, tenemos 8192/160 = 51.2 bloques, y CEIL (51.2) = 52 bloques requeridos para PBKDF2, es decir, T_1 a T_52 (l = 52). Las referencias de especificaciones a lo que sucede con .2 de manera diferente a un bloque completo están fuera del alcance de esta discusión (pista: truncamiento después de que se calcula el resultado completo).
Por lo tanto, está ejecutando un conjunto de 1000 iteraciones en su contraseña un total de 52 veces y concatenando la salida. Por lo tanto, para una contraseña, ¡en realidad está ejecutando 52000 iteraciones!
Un atacante inteligente ejecutará solo 1000 iteraciones y comparará su resultado de 160 bits con los primeros 160 bits de sus 8192 bits de salida; si falla, es una suposición incorrecta, continúe. Si tiene éxito, es casi seguro que sea una suposición (ataque) exitosa.
Por lo tanto, está ejecutando 52,000 iteraciones en una CPU, y un atacante está ejecutando 1,000 iteraciones en lo que sea que tenga (probablemente algunas GPU, que superan enormemente a su CPU para SHA-1 en primer lugar); les ha dado a los atacantes una ventaja de 52: 1 por encima de las ventajas del hardware.
Afortunadamente, una buena función PBKDF2 es fácil de ajustar; simplemente cambie su longitud de salida a 160 bits y su número de iteraciones a 52,000, y usará la misma cantidad de tiempo de CPU, almacene más pequeño keys¡Y hacerlo 52 veces más caro para cualquier atacante sin costo de tiempo de ejecución para usted!
Si desea nerfear aún más a los atacantes con GPU, es posible que desee cambiar a PBKDF2-HMAC-SHA-512 (o scrypt o bcrypt) y un tamaño de salida de 64 bytes o menos (el tamaño de salida nativo de SHA-512), lo que reduce significativamente la cantidad de ventaja que tienen las GPU actuales (principios de 2014) sobre las CPU debido a que las instrucciones de 64 bits están en las CPU pero no en las GPU. Sin embargo, esto no está disponible de forma nativa en .NET 4.5.
- Sin embargo, @Jither creó un buen ejemplo de cómo agregar PBKDF2-HMAC-SHA256, PBKDF2-HMAC-SHA384, PBKDF2-HMAC-SHA512, etc. a .NET, y he incluido una variante con un conjunto razonable de vectores de prueba en mi repositorio de Github como referencia.
Para obtener otra referencia relacionada con un defecto de diseño real en 1Password, consulte este hilo del foro de Hashcat: “Para cada iteración de PBKDF2-HMAC-SHA1, llama 4 veces la transformación SHA1. Pero esto es solo para producir un 160 bit key. Para producir el 320 bit requerido key, lo llamas 8 veces “.
PD: si lo desea, para un pequeño cambio de diseño, puede ahorrar un poco más de espacio en la base de datos si almacena la salida en una columna VARBINARY o BINARY en lugar de realizar la codificación Base64.
PPS, es decir, cambie el código de prueba en su edición de la siguiente manera (2000 * 52 = 104000); tenga en cuenta que su texto dijo 1000 y su prueba enumeró 2000, así que texto a texto y código a código, así sea.
//var hash = libCrypto.HashEncode2(password, salt, 2000);
var hash = libCrypto.HashEncode2(password, salt, 104000);
// skip down into HashEncode2
//byte[] hash = deriver.GetBytes(1024);
byte[] hash = deriver.GetBytes(20);
La familia SHA2 no es una buena opción para el almacenamiento de contraseñas. Es significativamente mejor que md5, pero realmente debería usar bcrypt (¡o scrypt!).
RNGCryptoServiceProvider es una buena fuente de entropía. Idealmente, una sal no es la base 64, sino la base 256, como en un byte completo. Para entender esto mejor, necesita saber cómo se generan las tablas de arco iris. La entrada a la generación de tablas de arco iris requiere un espacio de claves. Por ejemplo, se podría generar un arco iris para buscar: símbolo alfanumérico de 7 a 12 caracteres de longitud. Ahora, para descifrar este esquema de sal propuesto, el atacante tendría que generar un símbolo alfanumérico con 71-76 caracteres de longitud para compensar la sal de 64 caracteres (que es grande). Convertir la sal en un byte completo aumentaría enormemente el espacio de teclas que la tabla del arco iris tendría que agotar.
Acuérdate de que tienes permiso de interpretar tu experiencia si te fue de ayuda.