Na semana passada, li muitos artigos sobre hashing de senha e Blowfish parece ser (um dos) melhores algoritmos de hashing agora - mas esse não é o assunto desta pergunta!
O limite de 72 caracteres
Blowfish considera apenas os primeiros 72 caracteres na senha inserida:
<?php
$password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)";
$hash = password_hash($password, PASSWORD_BCRYPT);
var_dump($password);
$input = substr($password, 0, 72);
var_dump($input);
var_dump(password_verify($input, $hash));
?>
O resultado é:
string(119) "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)"
string(72) "Wow. This is a super secret and super, super long password. Let's add so"
bool(true)
Como você pode ver, apenas os primeiros 72 caracteres são importantes. O Twitter está usando blowfish aka bcrypt para armazenar suas senhas ( https://shouldichangemypassword.com/twitter-hacked.php ) e adivinhe: mude sua senha do Twitter para uma senha longa com mais de 72 caracteres e você pode fazer login em sua conta por inserindo apenas os primeiros 72 caracteres.
Baiacu e pimenta
Existem muitas opiniões diferentes sobre "salpicar" senhas. Algumas pessoas dizem que é desnecessário, porque você tem que assumir que a string secreta também é conhecida / publicada, de modo que não aprimora o hash. Eu tenho um servidor de banco de dados separado, então é bem possível que apenas o banco de dados esteja vazando e não a pimenta constante.
Neste caso (pimenta não vazou) você torna um ataque baseado em um dicionário mais difícil (me corrija se não estiver certo). Se o seu barbante de pimenta também vazou: nada mal - você ainda tem o sal e está tão bem protegido quanto um haxixe sem pimenta.
Portanto, acho que definir a senha pelo menos não é uma escolha ruim.
Sugestão
Minha sugestão para obter um hash Blowfish para uma senha com mais de 72 caracteres (e pimenta) é:
<?php
$pepper = "foIwUVmkKGrGucNJMOkxkvcQ79iPNzP5OKlbIdGPCMTjJcDYnR";
// Generate Hash
$password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)";
$password_peppered = hash_hmac('sha256', $password, $pepper);
$hash = password_hash($password_peppered, PASSWORD_BCRYPT);
// Check
$input = substr($password, 0, 72);
$input_peppered = hash_hmac('sha256', $input, $pepper);
var_dump(password_verify($input_peppered, $hash));
?>
Isso se baseia nesta pergunta : password_verify
retornar false
.
A questão
Qual é a maneira mais segura? Obter um hash SHA-256 primeiro (que retorna 64 caracteres) ou considerar apenas os primeiros 72 caracteres da senha?
Prós
- O usuário não pode fazer o login inserindo apenas os primeiros 72 caracteres
- Você pode adicionar pimenta sem exceder o limite de caracteres
- A saída de hash_hmac provavelmente teria mais entropia do que a própria senha
- A senha é hash por duas funções diferentes
Contras
- Apenas 64 caracteres são usados para construir o hash baiacu
Edit 1: Esta questão aborda apenas a integração PHP do blowfish / bcrypt. Obrigado pelos comentários!
Respostas:
O problema aqui é basicamente um problema de entropia. Então, vamos começar a procurar lá:
Entropia por personagem
O número de bits de entropia por byte são:
Então, como agimos depende do tipo de personagem que esperamos.
O primeiro problema
O primeiro problema com seu código é que sua etapa de hash "pepper" está gerando caracteres hexadecimais (já que o quarto parâmetro para
hash_hmac()
não está definido).Portanto, ao inserir a pimenta, você está efetivamente cortando a entropia máxima disponível para a senha por um fator de 2 (de 576 a 288 bits possíveis ).
O segundo problema
No entanto, em primeiro lugar ,
sha256
fornece apenas256
bits de entropia. Portanto, você está efetivamente reduzindo possíveis 576 bits para 256 bits. Seu hash step * imediatamente *, por definição perde pelo menos 50% da entropia possível na senha.Você poderia resolver isso parcialmente mudando para
SHA512
, onde reduziria a entropia disponível em cerca de 12%. Mas essa ainda é uma diferença não insignificante. Esses 12% reduzem o número de permutações por um fator de1.8e19
. É um grande número ... E esse é o fator que o reduz em ...O problema subjacente
O problema subjacente é que existem três tipos de senhas com mais de 72 caracteres. O impacto que este sistema de estilo tem sobre eles será muito diferente:
Nota: de agora em diante, estou assumindo que estamos comparando a um sistema de pimenta que usa
SHA512
com saída bruta (não hex).Senhas aleatórias de alta entropia
Estes são seus usuários usando geradores de senha que geram a quantidade de chaves grandes para senhas. Eles são aleatórios (gerados, não escolhidos por humanos) e têm alta entropia por personagem. Esses tipos usam bytes altos (caracteres> 127) e alguns caracteres de controle.
Para este grupo, sua função de hashing reduzirá significativamente a entropia disponível em
bcrypt
.Deixe-me dizer isso de novo. Para usuários que usam senhas longas e de alta entropia, sua solução reduz significativamente a força de suas senhas em uma quantidade mensurável. (62 bits de entropia perdidos para uma senha de 72 caracteres e mais para senhas mais longas)
Senhas aleatórias de entropia média
Este grupo está usando senhas contendo símbolos comuns, mas sem bytes altos ou caracteres de controle. Estas são suas senhas digitáveis.
Para este grupo, você irá desbloquear um pouco mais entropia (não criá-la, mas permitir que mais entropia caiba na senha bcrypt). Quando digo levemente, quero dizer levemente. O ponto de equilíbrio ocorre quando você atinge o máximo de 512 bits do SHA512. Portanto, o pico é de 78 caracteres.
Deixe-me dizer isso de novo. Para esta classe de senhas, você só pode armazenar 6 caracteres adicionais antes de esgotar a entropia.
Senhas não aleatórias de baixa entropia
Este é o grupo que está usando caracteres alfanuméricos que provavelmente não são gerados aleatoriamente. Algo como uma citação da Bíblia ou algo assim. Essas frases têm aproximadamente 2,3 bits de entropia por caractere.
Para este grupo, você pode desbloquear significativamente mais entropia (não criá-la, mas permitir que mais entropia caiba na entrada de senha bcrypt) por hash. O ponto de equilíbrio é de cerca de 223 caracteres antes de esgotar a entropia.
Vamos dizer isso de novo. Para essa classe de senhas, o pré-hashing definitivamente aumenta a segurança de forma significativa.
De volta ao mundo real
Esses tipos de cálculos de entropia não importam muito no mundo real. O que importa é adivinhar a entropia. Isso é o que afeta diretamente o que os invasores podem fazer. Isso é o que você deseja maximizar.
Embora haja pouca pesquisa para adivinhar a entropia, há alguns pontos que gostaria de apontar.
As chances de adivinhar aleatoriamente 72 caracteres corretos em uma linha são extremamente baixas. É mais provável que você ganhe na loteria Powerball 21 vezes do que essa colisão ... Esse é o número de que estamos falando.
Mas podemos não tropeçar nisso estatisticamente. No caso de frases, a chance dos primeiros 72 caracteres serem iguais é muito maior do que para uma senha aleatória. Mas ainda é trivialmente baixo (é mais provável que você ganhe na loteria Powerball 5 vezes, com base em 2,3 bits por personagem).
Praticamente
Praticamente, isso realmente não importa. As chances de alguém adivinhar os primeiros 72 caracteres corretamente, onde os últimos fazem uma diferença significativa, são tão baixas que não vale a pena se preocupar. Por quê?
Bem, digamos que você esteja pegando uma frase. Se a pessoa conseguir acertar os primeiros 72 caracteres, ela tem muita sorte (pouco provável) ou é uma frase comum. Se for uma frase comum, a única variável é quanto tempo deve ser produzida.
Vamos dar um exemplo. Vamos fazer uma citação da Bíblia (só porque é uma fonte comum de textos longos, não por qualquer outro motivo):
São 180 caracteres. O 73º personagem é
g
o segundoneighbor's
. Se você adivinhou isso, provavelmente não está parando emnei
, mas continuando com o resto do versículo (já que é assim que a senha provavelmente será usada). Portanto, seu "hash" não acrescentou muito.BTW: ABSOLUTAMENTE NÃO estou defendendo o uso de uma citação da Bíblia. Na verdade, exatamente o oposto.
Conclusão
Você não vai ajudar muito as pessoas que usam senhas longas fazendo hash primeiro. Alguns grupos você definitivamente pode ajudar. Alguns você definitivamente pode machucar.
Mas no final, nada disso é excessivamente significativo. Os números com os quais estamos lidando são MUITO altos. A diferença na entropia não será muito.
É melhor você deixar bcrypt como está. É mais provável que você estrague o hashing (literalmente, você já fez isso e não é o primeiro ou o último a cometer esse erro) do que o ataque que está tentando evitar vai acontecer.
Concentre-se em proteger o resto do site. E adicione um medidor de entropia de senha à caixa de senha no registro para indicar a força da senha (e indicar se uma senha é longa demais para que o usuário queira alterá-la) ...
Isso é meu $ 0,02 pelo menos (ou possivelmente bem mais que $ 0,02) ...
Quanto a usar uma pimenta "secreta":
Não há literalmente nenhuma pesquisa sobre alimentar uma função hash em bcrypt. Portanto, não está claro, na melhor das hipóteses, se alimentar um hash "apimentado" em bcrypt algum dia causará vulnerabilidades desconhecidas (sabemos que isso
hash1(hash2($value))
pode expor vulnerabilidades significativas em relação à resistência à colisão e ataques de pré-imagem).Considerando que você já está pensando em armazenar uma chave secreta (a "pimenta"), por que não usá-la de uma forma bem estudada e compreendida? Por que não criptografar o hash antes de armazená-lo?
Basicamente, depois de fazer o hash da senha, coloque toda a saída do hash em um algoritmo de criptografia forte. Em seguida, armazene o resultado criptografado.
Agora, um ataque de injeção de SQL não vazará nada de útil, porque eles não têm a chave de criptografia. E se a chave vazar, os invasores não estarão em melhor situação do que se você usasse um hash simples (o que é provável, algo com a pimenta "pré-hash" não fornece).
Nota: se você decidir fazer isso, use uma biblioteca. Para PHP, eu recomendo fortemente o
Zend\Crypt
pacote do Zend Framework 2 . Na verdade, é o único que eu recomendaria neste momento. Foi fortemente revisto e toma todas as decisões por você (o que é uma coisa muito boa) ...Algo como:
E é benéfico porque você está usando todos os algoritmos de maneiras que são bem compreendidas e bem estudadas (pelo menos relativamente). Lembrar:
fonte
Melhorar as senhas é certamente uma boa coisa a se fazer, mas vamos ver por quê.
Primeiro devemos responder à pergunta quando exatamente uma pimenta ajuda. A pimenta protege apenas as senhas, desde que permaneça secreta, portanto, se um invasor tiver acesso ao próprio servidor, não adianta. Um ataque muito mais fácil é a injeção de SQL, que permite acesso de leitura ao banco de dados (para nossos valores de hash), eu preparei uma demonstração de injeção de SQL para mostrar como pode ser fácil (clique na próxima seta para obter um entrada).
Então, o que a pimenta realmente ajuda? Enquanto a pimenta permanecer secreta, ela protege as senhas fracas de um ataque de dicionário. A senha
1234
se tornaria algo assim1234-p*deDIUZeRweretWy+.O
. Esta senha não só é muito mais longa, como também contém caracteres especiais e nunca fará parte de nenhum dicionário.Agora podemos estimar quais senhas nossos usuários usarão, provavelmente mais usuários digitarão senhas fracas, pois há usuários com senhas entre 64-72 caracteres (na verdade, isso será muito raro).
Outro ponto é o alcance da força bruta. A função hash sha256 retornará 256 bits de saída ou combinações de 1.2E77, isso é demais para força bruta, mesmo para GPU (se eu calculasse corretamente, isso precisaria de cerca de 2E61 anos em uma GPU em 2013). Portanto, não temos uma desvantagem real ao aplicar a pimenta. Como os valores hash não são sistemáticos, você não pode acelerar a força bruta com padrões comuns.
PS Pelo que eu sei, o limite de 72 caracteres é específico do algoritmo do próprio BCrypt. A melhor resposta que encontrei é esta .
PPS Acho que seu exemplo está falho, você não pode gerar o hash com o comprimento total da senha e verificar com um truncado. Você provavelmente pretendia aplicar a pimenta da mesma maneira para gerar o hash e para verificar o hash.
fonte
true
. É disso que trata esta questão. Dê uma olhada: viper-7.com/RLKFnBBcrypt usa um algoritmo baseado no caro algoritmo de configuração de chave Blowfish.
O limite de senha de 56 bytes recomendado (incluindo byte de terminação nulo) para bcrypt está relacionado ao limite de 448 bits da chave Blowfish. Quaisquer bytes além desse limite não são totalmente misturados ao hash resultante. O limite absoluto de 72 bytes em senhas bcrypt é, portanto, menos relevante, quando você considera o efeito real no hash resultante por esses bytes.
Se você acha que seus usuários normalmente escolheriam senhas com mais de 55 bytes de comprimento, lembre-se de que você sempre pode aumentar as rodadas de extensão de senha, para aumentar a segurança no caso de violação da tabela de senha (embora isso deva ser muito comparado com adicionar extras personagens). Se os direitos de acesso dos usuários forem tão críticos que normalmente exigiriam uma senha muito longa, a expiração da senha também deveria ser curta, como 2 semanas. Isso significa que é muito menos provável que uma senha permaneça válida enquanto um hacker investe seus recursos para derrotar o fator de trabalho envolvido no teste de cada senha de teste para ver se ela produzirá um hash correspondente.
Claro, no caso de a tabela de senhas não ser violada, devemos permitir que os hackers, no máximo, dez tentativas de adivinhar a senha de 55 bytes de um usuário, antes de bloquear a conta do usuário;)
Se você decidir fazer o pré-hash de uma senha com mais de 55 bytes, deverá usar SHA-384, pois tem a maior saída sem ultrapassar o limite.
fonte