Qual tipo / comprimento da coluna devo usar para armazenar uma senha com hash Bcrypt em um banco de dados?

318

Quero armazenar uma senha com hash (usando BCrypt) em um banco de dados. Qual seria um bom tipo para isso e qual seria o comprimento correto? As senhas com hash BCrypt são sempre do mesmo tamanho?

EDITAR

Hash de exemplo:

$2a$10$KssILxWNR6k62B7yiX0GAe2Q7wwHlrzhF3LqtVvpyvHZf0MwvNfVu

Após o hash de algumas senhas, parece que o BCrypt sempre gera 60 hashes de caracteres.

EDIT 2

Desculpe por não mencionar a implementação. Estou usando o jBCrypt .

helpermethod
fonte
Veja também a estrutura de hash de senha PHP do Openwall (PHPass). É portátil e reforçado contra uma série de ataques comuns a senhas de usuários. O cara que escreveu a estrutura (SolarDesigner) é o mesmo que escreveu John The Ripper e é juiz na Competição de Hashing de Senhas . Então ele sabe uma coisa ou duas sobre ataques a senhas.
JWW
1
Se alguém cair nessa, procurando uma solução para o scrypt : a resposta da Gumbo também se aplica ao scrypt. Eu pessoalmente apliquei BINARY (64) no MySQL e me permitiu testar a igualdade de bytes no Python posteriormente.
Philippe Hebert #

Respostas:

369

O formato modular de criptografia para bcrypt consiste em

  • $2$, $2a$Ou $2y$identificar o algoritmo de hash e formato
  • um valor de dois dígitos que indica o parâmetro cost, seguido por $
  • A 53 caracteres valor base 64 codificada (que utilizam o alfabeto ., /, 0- 9, A- Z, a- z, que é diferente para a base de dados de padrão 64 codificação alfabeto) consistindo de:
    • 22 caracteres de sal (efetivamente apenas 128 bits dos 132 bits decodificados)
    • 31 caracteres de saída criptografada (efetivamente apenas 184 bits dos 186 bits decodificados)

Assim, o comprimento total é de 59 ou 60 bytes, respectivamente.

Ao usar o formato 2a, você precisará de 60 bytes. E, portanto, para o MySQL, recomendo usar o CHAR(60) BINARYorBINARY(60) (consulte The _bin e binary Collations para obter informações sobre a diferença).

CHARnão é binário seguro e a igualdade não depende apenas do valor do byte, mas do agrupamento real; no pior dos casos, Aé tratado como igual a a. Consulte The _bine binaryCollations para obter mais informações.

quiabo
fonte
28
Esteja ciente de que armazenar como binário (60) pode causar um comportamento inesperado para a igualdade de cadeias (entre outras coisas). Em .NET isso pode ser superado usando String.Equals (fromDataBaseBinary60string, typicalishString, StringComparison.InvariantCulture)
JHubbard80
8
Se você definir a coluna como CHAR (60) CHARACTER SET latin1 COLLATE latin1_bin, agora obterá as vantagens da comparação precisa de cadeias sem precisar de uma coluna binária.
Ben
2
@AndreFigueiredo SQL_Latin1_General_CP1_CS_ASé desconhecido no MySQL. O que se sabe é latin1_general_cs.
Gumbo
1
Eu adoraria ter uma definição aqui para o que 2, 2ae 2ymédia para algoritmo de hash e formato. Não consegui encontrar uma resposta fácil com algumas pesquisas.
jocull
2
@ Neon O problema é que você pode comparar diferentes hashes para serem iguais. Se você especificar explicitamente que é uma coluna binária (ou um VARCHAR com o agrupamento correto), não corre o risco de, em outro lugar, alterar alguma configuração que a torne uma comparação que não diferencia maiúsculas de minúsculas. Também deixa sua intenção mais clara, o que geralmente é uma coisa boa - você está armazenando dados binários; você deve armazená-lo como dados binários.
Fund Monica's Lawsuit
52

Um hash Bcrypt pode ser armazenado em um BINARY(40) coluna.

BINARY(60), como as outras respostas sugerem, é a escolha mais fácil e mais natural, mas se você deseja maximizar a eficiência do armazenamento, pode salvar 20 bytes desconstruindo sem perdas o hash. Documentei isso mais detalhadamente no GitHub: https://github.com/ademarre/binary-mcf

Os hashes Bcrypt seguem uma estrutura chamada de formato de criptografia modular (MCF). Binário MCF (BMCF) decodifica essas representações de hash textuais para uma estrutura binária mais compacta. No caso de Bcrypt, o hash binário resultante é de 40 bytes.

O Gumbo fez um bom trabalho ao explicar os quatro componentes de um hash do Bcrypt MCF:

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

A decodificação para BMCF é assim:

  1. $<id>$ pode ser representado em 3 bits.
  2. <cost>$, 04-31, pode ser representado em 5 bits. Coloque-os juntos por 1 byte.
  3. O sal de 22 caracteres é uma representação de base 64 (não padrão) de 128 bits. A decodificação Base-64 produz 16 bytes.
  4. O resumo de hash de 31 caracteres pode ser decodificado em base-64 para 23 bytes.
  5. Junte tudo por 40 bytes: 1 + 16 + 23

Você pode ler mais no link acima ou examinar minha implementação do PHP , também no GitHub.

E vermelho
fonte
49
Custo do campo mais longo: 20 bytes vezes até um milhão de registros: 20 MB, quando você atingir um milhão de registros. Custo da implementação incorreta de um tamanho reduzido de campo, em um campo altamente complexo de segurança e engenharia: $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Você faz as contas.
Kzqai #
6
@Kzqai, como eu disse, a coluna maior de 60 bytes é a escolha mais natural, mas a agressividade em buscar a eficiência do armazenamento depende do projeto. Por exemplo, é comum tentar ajustar o banco de dados inteiro na memória, e 20 MB aqui e outros 20 ali podem ser adicionados rapidamente em um ambiente com restrição de memória.
Andre D
10
Seu exemplo alimenta meu argumento. --- Se você deseja colocar seu banco de dados na memória, otimize todas as outras colunas antes de tocar na coluna de armazenamento bcrypt. --- Se você otimizou todas as outras colunas para graus insanos, e apenas a coluna de hash bcrypt é deixada, obtenha outro gig de memória apenas para bcrypt. --- Se você fez as duas opções acima ... ... parar, não otimizou todas as outras colunas de frutas baixas e está prestes a mexer com um sistema de segurança criptográfico testado que funciona e substitui com um sistema caseiro mais complicado, com chance de falha na implementação.
Kzqai #
11
@Kzqai Não há risco de enfraquecer a segurança da sua biblioteca Bcrypt aqui. É uma codificação de dados que é desfeita na recuperação do armazenamento antes da verificação de senha. Este não é um território "não role sua própria criptografia".
Andre D
1
Boa explicação. :) Embora sua explicação tenha sido uma ótima idéia, eu só quero usar 60 caracteres, mesmo 100 caracteres, apenas para estar do lado seguro. Bom debate muito @Kzqai e Andred
Naveen Kumar V
23

Se você estiver usando PHP password_hash()com o PASSWORD_DEFAULTalgoritmo para gerar o hash bcrypt (o que eu assumiria que é uma grande porcentagem de pessoas lendo esta pergunta), lembre-se de que, no futuro, password_hash()poderá usar um algoritmo diferente como padrão e, portanto, poderá afeta o comprimento do hash (mas pode não necessariamente ser mais longo).

Na página do manual:

Observe que essa constante foi projetada para mudar com o tempo, à medida que novos e mais fortes algoritmos são adicionados ao PHP. Por esse motivo, a duração do resultado do uso desse identificador pode mudar com o tempo. Portanto, é recomendável armazenar o resultado em uma coluna do banco de dados que possa ser expandida além de 60 caracteres (255 caracteres seria uma boa opção).

Usando bcrypt, mesmo se você tiver 1 bilhão de usuários (ou seja, atualmente você está competindo com o facebook) para armazenar hashes de senhas de 255 bytes, seriam apenas ~ 255 GB de dados - aproximadamente o tamanho de um pequeno disco rígido SSD. É extremamente improvável que armazenar o hash da senha seja o gargalo no seu aplicativo. No entanto, na possibilidade de que o espaço de armazenamento seja realmente um problema por algum motivo, você pode PASSWORD_BCRYPTforçar o password_hash()uso do bcrypt, mesmo que esse não seja o padrão. Certifique-se de manter-se informado sobre quaisquer vulnerabilidades encontradas no bcrypt e revise as notas de versão sempre que uma nova versão do PHP for lançada. Se o algoritmo padrão for alterado, seria bom revisar o motivo e tomar uma decisão informada sobre o uso ou não do novo algoritmo.

Mike
fonte
20

Eu não acho que existem truques legais que você possa fazer para armazenar isso, como você pode, por exemplo, com um hash MD5.

Eu acho que a sua melhor aposta é armazená-lo como CHAR(60)sempre, com 60 caracteres

James C
fonte
Embora, as notas de documentação PHP que colunas deve ser capaz de armazenar mais dados, para futuros lançamentos ...
Julian F. Weinert
16
Não há razão para placa de ouro. Se o software que você está usando exigir sessenta bytes, aloque sessenta bytes. Se houver uma versão futura do seu software que altere isso, você poderá se preocupar com isso quando essa versão acontecer. Você não deve instalar automaticamente atualizações que alteram a funcionalidade.
Tyler Crompton