¿Qué tipo / longitud de columna debo usar para almacenar una contraseña hash de Bcrypt en una base de datos?

318

Quiero almacenar una contraseña hash (usando BCrypt) en una base de datos. ¿Cuál sería un buen tipo para esto y cuál sería la longitud correcta? ¿Las contraseñas hash con BCrypt son siempre de la misma longitud?

EDITAR

Ejemplo hash:

$2a$10$KssILxWNR6k62B7yiX0GAe2Q7wwHlrzhF3LqtVvpyvHZf0MwvNfVu

Después de cifrar algunas contraseñas, parece que BCrypt siempre genera 60 hashes de caracteres.

EDITAR 2

Perdón por no mencionar la implementación. Estoy usando jBCrypt .

método de ayuda
fuente
Consulte también el marco de hashing de contraseñas PHP de Openwall (PHPass). Es portátil y reforzado contra una serie de ataques comunes a las contraseñas de los usuarios. El tipo que escribió el marco (SolarDesigner) es el mismo tipo que escribió John The Ripper y se sienta como juez en la Competencia de hash de contraseñas . Entonces sabe una o dos cosas sobre los ataques a las contraseñas.
jww
1
Si alguien cae en esto buscando una solución para scrypt : la respuesta de Gumbo también se aplica a scrypt. Personalmente apliqué BINARY (64) en MySQL y más tarde me permitió probar la igualdad de bytes en Python.
Philippe Hebert,

Respuestas:

370

El formato de cripta modular para bcrypt consiste en

  • $2$, $2a$o $2y$identificando el algoritmo y el formato hash
  • un valor de dos dígitos que denota el parámetro de costo, seguido de $
  • a 53 caracteres de largo valor base-64-encoded (utilizan el alfabeto ., /, 0- 9, A- Z, a- zque es diferente a la base estándar 64 Codificación alfabeto) que consiste en:
    • 22 caracteres de sal (efectivamente solo 128 bits de los 132 bits decodificados)
    • 31 caracteres de salida cifrada (efectivamente solo 184 bits de los 186 bits decodificados)

Por lo tanto, la longitud total es de 59 o 60 bytes, respectivamente.

Cuando use el formato 2a, necesitará 60 bytes. Y por lo tanto para MySQL Voy a recomendar a utilizar el CHAR(60) BINARYoBINARY(60) (ver El _bin y binarios colaciones para obtener información acerca de la diferencia).

CHARno es binario seguro y la igualdad no depende únicamente del valor del byte sino de la recopilación real; en el peor de los casos Ase trata como igual a a. Vea The _binand binaryCollations para más información.

Gumbo
fuente
28
Tenga en cuenta que el almacenamiento como binario (60) puede causar un comportamiento inesperado para la igualdad de cadenas (entre otras cosas). En .NET esto se puede superar mediante el uso de String.Equals (fromDataBaseBinary60string, typicalishString, StringComparison.InvariantCulture)
JHubbard80
8
Si define la columna como CHAR (60) CHARACTER SET latin1 COLLATE latin1_bin, ahora obtendrá las ventajas de una comparación de cadenas precisa sin necesidad de una columna binaria.
Ben
2
@AndreFigueiredo SQL_Latin1_General_CP1_CS_ASes desconocido en MySQL. Lo que se sabe es latin1_general_cs.
Gumbo
1
Me encantaría tener una definición aquí por lo que 2, 2ay 2ymedia para el algoritmo de hash y el formato. No pude encontrar una respuesta fácil con algunas búsquedas.
jocull
2
@Neon El problema es que puede comparar diferentes hashes para que sean iguales. Si especifica explícitamente que es una columna binaria (o un VARCHAR con la clasificación correcta), no corre el riesgo de, en otro lugar, cambiar alguna configuración que lo haga una comparación que no distingue entre mayúsculas y minúsculas. También hace que su intención sea más clara, lo que generalmente es algo bueno: está almacenando datos binarios; debe almacenarlo como datos binarios.
Financia la demanda de Mónica
52

Un hash Bcrypt se puede almacenar en una BINARY(40)columna.

BINARY(60), como sugieren las otras respuestas, es la opción más fácil y natural, pero si desea maximizar la eficiencia del almacenamiento, puede ahorrar 20 bytes al deconstruir el hash sin pérdidas. He documentado esto más a fondo en GitHub: https://github.com/ademarre/binary-mcf

Los hash de Bcrypt siguen una estructura denominada formato de cripta modular (MCF). Binary MCF (BMCF) decodifica estas representaciones hash textuales en una estructura binaria más compacta. En el caso de Bcrypt, el hash binario resultante es de 40 bytes.

Gumbo hizo un buen trabajo al explicar los cuatro componentes de un hash Bcrypt MCF:

$<id>$<cost>$<salt><digest>

La decodificación a BMCF es así:

  1. $<id>$ Se puede representar en 3 bits.
  2. <cost>$, 04-31, se puede representar en 5 bits. Pon estos juntos por 1 byte.
  3. La sal de 22 caracteres es una representación (no estándar) de base 64 de 128 bits. La decodificación de base 64 produce 16 bytes.
  4. El resumen de hash de 31 caracteres se puede decodificar en base 64 a 23 bytes.
  5. Ponlo todo junto para 40 bytes: 1 + 16 + 23

Puede leer más en el enlace de arriba, o examinar mi implementación de PHP , también en GitHub.

Y Rojo
fuente
49
Costo del campo más largo: 20 bytes por incluso más de un millón de registros: 20 MB, una vez que llegue a un millón de registros +. Costo de implementar incorrectamente una longitud de campo acortada, en un campo de seguridad e ingeniería altamente complejo: $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$ Usted hace los cálculos.
Kzqai
66
@Kzqai, como dije, la columna más grande de 60 bytes es la opción más natural, pero la forma agresiva de lograr la eficiencia del almacenamiento depende del proyecto. Por ejemplo, es común tratar de ajustar toda la base de datos en la memoria, y 20 MB aquí y otros 20 allí pueden acumularse rápidamente en un entorno con memoria limitada.
Andre D
10
Tu ejemplo alimenta mi punto. --- Si desea poner su base de datos en la memoria, optimice cada dos columnas antes de tocar la columna de almacenamiento de bcrypt. --- Si ha optimizado cada dos columnas en grados insanos, y solo queda la columna hash de bcrypt, obtenga otro concierto de memoria solo para bcrypt. --- Si ha hecho las dos cosas anteriores ... ... deténgase, no ha optimizado todas las demás columnas de fruta baja y está a punto de meterse con un sistema de seguridad criptográfico probado que funciona y reemplazar con un sistema interno más complicado con una posibilidad de falla de implementación.
Kzqai
11
@Kzqai Aquí no hay riesgo de debilitar la seguridad de su biblioteca Bcrypt. Es una codificación de datos que se deshace en la recuperación del almacenamiento antes de la verificación de la contraseña. Este no es el territorio de "no tires tu propia criptografía".
Andre D
1
Buena explicación :) Aunque su explicación dio una gran idea, solo quiero ir con 60 caracteres, incluso 100 caracteres, solo para estar seguro. Buen debate también @Kzqai y AndreD
Naveen Kumar V
23

Si está utilizando PHP password_hash()con el PASSWORD_DEFAULTalgoritmo para generar el hash bcrypt (que supongo que es un gran porcentaje de personas que leen esta pregunta) asegúrese de tener en cuenta que en el futuro password_hash()podría usar un algoritmo diferente como predeterminado y esto podría afecta la longitud del hash (pero puede que no sea necesariamente más largo).

Desde la página del manual:

Tenga en cuenta que esta constante está diseñada para cambiar con el tiempo a medida que se agregan algoritmos nuevos y más fuertes a PHP. Por esa razón, la longitud del resultado del uso de este identificador puede cambiar con el tiempo. Por lo tanto, se recomienda almacenar el resultado en una columna de base de datos que pueda expandirse más allá de 60 caracteres (255 caracteres sería una buena opción).

Usando bcrypt, incluso si tiene mil millones de usuarios (es decir, actualmente está compitiendo con Facebook) para almacenar hashes de contraseñas de 255 bytes, solo necesitaría ~ 255 GB de datos, aproximadamente del tamaño de un disco duro SSD más pequeño. Es extremadamente improbable que almacenar el hash de contraseña sea el cuello de botella en su aplicación. Sin embargo, en caso de que el espacio de almacenamiento realmente sea un problema por alguna razón, puede usar PASSWORD_BCRYPTforzar el password_hash()uso de bcrypt, incluso si ese no es el valor predeterminado. Solo asegúrese de mantenerse informado sobre las vulnerabilidades encontradas en bcrypt y revise las notas de la versión cada vez que se lance una nueva versión de PHP. Si alguna vez se cambia el algoritmo predeterminado, sería bueno revisar por qué y tomar una decisión informada sobre si usar el nuevo algoritmo o no.

Miguel
fuente
20

No creo que haya algunos trucos geniales que pueda hacer para almacenar esto, como puede hacer, por ejemplo, con un hash MD5.

Creo que su mejor opción es almacenarlo CHAR(60)ya que siempre tiene 60 caracteres de largo

James C
fuente
Aunque, la documentación de PHP señala que las columnas deberían poder contener más datos, para futuras versiones ...
Julian F. Weinert
16
No hay razón para la placa de oro. Si el software que está utilizando requiere sesenta bytes, asigne sesenta bytes. Si hay una versión futura de su software que cambia esto, entonces puede preocuparse cuando ocurra esa versión. No debería instalar automáticamente actualizaciones que cambian la funcionalidad.
Tyler Crompton