Hash e salt seguros para senhas PHP

1174

Diz-se atualmente que o MD5 é parcialmente inseguro. Levando isso em consideração, gostaria de saber qual mecanismo usar para proteção de senha.

Esta pergunta, o "hash duplo" é uma senha menos segura do que apenas uma vez? sugere que o hash várias vezes pode ser uma boa ideia, enquanto Como implementar a proteção por senha para arquivos individuais? sugere o uso de sal.

Estou usando PHP. Eu quero um sistema de criptografia de senha seguro e rápido. Hashing de uma senha um milhão de vezes pode ser mais seguro, mas também mais lento. Como conseguir um bom equilíbrio entre velocidade e segurança? Além disso, eu prefiro que o resultado tenha um número constante de caracteres.

  1. O mecanismo de hash deve estar disponível em PHP
  2. Deve ser seguro
  3. Ele pode usar sal (neste caso, todos os sais são igualmente bons? Existe alguma maneira de gerar bons sais?)

Além disso, devo armazenar dois campos no banco de dados (um usando MD5 e outro usando SHA, por exemplo)? Tornaria mais seguro ou não seguro?

Caso eu não tenha sido claro o suficiente, quero saber quais funções de hash usar e como escolher um bom sal para ter um mecanismo de proteção por senha rápido e seguro.

Perguntas relacionadas que não cobrem completamente minha pergunta:

Qual é a diferença entre SHA e MD5 no PHP
Simple Password Encryption
Métodos seguros de armazenamento de chaves, senhas para o asp.net
Como você implementaria senhas salgadas no Tomcat 5.5

luiscubal
fonte
13
openwall.com/phpass também é muito boa biblioteca
Alfred
51
MD5 é agora completamente inseguro
JqueryToAddNumbers
3
@NSAwesomeGuy Isso depende do que você está usando. É trivial combinar senhas MD5 sem correspondência de força bruta ou com força bruta, com certeza, mas com uma decência salgada ainda é extremamente impraticável criar uma tabela de arco-íris para quebra rápida de conjuntos de senhas, e a força bruta é um incômodo.
Craig Ringer
12
PHP 5.5+ têm um hash de senha segura construído em php.net/manual/en/function.password-hash.php
Terence Johnson

Respostas:

982

AVISO LEGAL : Esta resposta foi escrita em 2008.

Desde então, o PHP nos forneceu password_hashe password_verifye, desde a sua introdução, eles são o método recomendado de hash e verificação de senha.

A teoria da resposta ainda é uma boa leitura.

TL; DR

Não é

  • Não limite os caracteres que os usuários podem inserir para senhas. Somente idiotas fazem isso.
  • Não limite o tamanho de uma senha. Se seus usuários quiserem uma frase com um significado supercalifragilístico e especial, não os impeça de usá-la.
  • Não retire ou escape HTML e caracteres especiais na senha.
  • Nunca armazene a senha do usuário em texto sem formatação.
  • Nunca envie uma senha para o seu usuário, exceto quando a perdeu, e você enviou uma senha temporária.
  • Nunca, nunca registre senhas de nenhuma maneira.
  • Nunca faça hash de senhas com SHA1 ou MD5 ou mesmo SHA256! Os crackers modernos podem exceder 60 e 180 bilhões de hashes / segundo (respectivamente).
  • Não misture bcrypt e com a saída bruta de hash () , use a saída hexadecimal ou base64_encode. (Isso se aplica a qualquer entrada que possa ter um desonesto \0, o que pode enfraquecer seriamente a segurança.)

Dos

  • Use scrypt quando puder; bcrypt se você não puder.
  • Use PBKDF2 se você não puder usar bcrypt ou scrypt, com hashes SHA2.
  • Redefina as senhas de todos quando o banco de dados estiver comprometido.
  • Implemente um comprimento mínimo razoável de 8 a 10 caracteres, além de exigir pelo menos 1 letra maiúscula, 1 letra minúscula, um número e um símbolo. Isso melhorará a entropia da senha, tornando mais difícil a quebra. (Consulte a seção "O que faz uma boa senha?" Para algum debate.)

Por que senhas hash, afinal?

O objetivo por trás do hash de senhas é simples: impedir o acesso malicioso a contas de usuários comprometendo o banco de dados. Portanto, o objetivo do hash de senha é impedir um hacker ou cracker, gastando muito tempo ou dinheiro para calcular as senhas em texto simples. E tempo / custo são os melhores impedimentos em seu arsenal.

Outro motivo pelo qual você deseja um hash bom e robusto nas contas de usuário é dar a você tempo suficiente para alterar todas as senhas no sistema. Se seu banco de dados estiver comprometido, você precisará de tempo suficiente para pelo menos bloquear o sistema, se não alterar todas as senhas no banco de dados.

Jeremiah Grossman, CTO da Whitehat Security, declarou no blog White Hat Security após uma recente recuperação de senha que exigia a quebra de força bruta de sua proteção por senha:

Curiosamente, ao viver esse pesadelo, aprendi MUITO que não sabia sobre quebra de senhas, armazenamento e complexidade. Aprendi a entender por que o armazenamento de senhas é muito mais importante que a complexidade das senhas. Se você não sabe como sua senha é armazenada, tudo em que realmente pode depender é da complexidade. Isso pode ser um conhecimento comum para profissionais de senhas e criptografia, mas para o especialista médio do InfoSec ou do Web Security, duvido muito.

(Ênfase minha.)

O que faz uma boa senha, afinal?

Entropia . (Não que eu assine totalmente o ponto de vista de Randall.)

Em resumo, entropia é quanta variação existe dentro da senha. Quando uma senha é apenas letras romanas em minúsculas, são apenas 26 caracteres. Isso não é muita variação. As senhas alfanuméricas são melhores, com 36 caracteres. Mas permitir maiúsculas e minúsculas, com símbolos, tem aproximadamente 96 caracteres. Isso é muito melhor do que apenas letras. Um problema é que, para tornar nossas senhas memoráveis, inserimos padrões - o que reduz a entropia. Opa!

A entropia de senha é aproximada facilmente. O uso de toda a gama de caracteres ascii (aproximadamente 96 caracteres digitáveis) gera uma entropia de 6,6 por caractere, que com 8 caracteres para uma senha ainda é muito baixa (52,679 bits de entropia) para segurança futura. Mas a boa notícia é: senhas mais longas e senhas com caracteres unicode aumentam realmente a entropia de uma senha e dificultam a quebra.

Há uma discussão mais longa sobre entropia de senha no site Crypto StackExchange . Uma boa pesquisa no Google também gera muitos resultados.

Nos comentários, conversei com o @popnoodles, que apontou que a imposição de uma política de senha com comprimento X com X muitas letras, números, símbolos etc. pode reduzir a entropia, na verdade, tornando o esquema de senha mais previsível. Eu concordo. A aleatoriedade, o mais aleatória possível, é sempre a solução mais segura, mas menos memorável.

Até onde eu sei, criar a melhor senha do mundo é um Catch-22. Não é memorável, previsível, muito curto, caracteres unicode demais (difíceis de digitar em um dispositivo Windows / Mobile), muito longo, etc. Nenhuma senha é realmente boa o suficiente para nossos propósitos, portanto, devemos protegê-los como se eles fossem estavam em Fort Knox.

Melhores Práticas

Bcrypt e scrypt são as melhores práticas atuais. O Scrypt será melhor do que o bcrypt a tempo, mas ainda não viu a adoção como padrão pelo Linux / Unix ou pelos servidores da web e ainda não teve análises detalhadas de seu algoritmo publicadas. Mas, ainda assim, o futuro do algoritmo parece promissor. Se você estiver trabalhando com Ruby, há uma gema scrypt que o ajudará, e o Node.js agora tem seu próprio pacote scrypt . Você pode usar o Scrypt no PHP via extensão Scrypt ou Libsodium (ambas estão disponíveis no PECL).

Eu recomendo a leitura da documentação para a função crypt, se você quiser entender como usar o bcrypt, encontrar um bom wrapper ou usar algo como PHPASS para uma implementação mais antiga . Eu recomendo um mínimo de 12 rodadas de bcrypt, se não 15 a 18.

Mudei de idéia sobre o uso do bcrypt quando soube que o bcrypt usa apenas o cronograma de chaves do blowfish, com um mecanismo de custo variável. O último permite aumentar o custo da força bruta de uma senha, aumentando a agenda de chaves já dispendiosa do blowfish.

Práticas médias

Quase não consigo mais imaginar essa situação. O PHPASS suporta PHP 3.0.18 a 5.3, portanto é utilizável em quase todas as instalações imagináveis ​​- e deve ser usado se você não tiver certeza de que seu ambiente suporta bcrypt.

Mas suponha que você não possa usar bcrypt ou PHPASS. O que então?

Tente uma implementação do PDKBF2 com o número máximo de rodadas que seu ambiente / aplicativo / percepção do usuário pode tolerar. O número mais baixo que eu recomendo é 2500 rodadas. Além disso, certifique-se de usar hash_hmac () se estiver disponível para dificultar a reprodução da operação.

Práticas Futuras

O PHP 5.5 é uma biblioteca completa de proteção por senha que abstrai qualquer esforço de trabalhar com o bcrypt. Enquanto a maioria de nós está presa ao PHP 5.2 e 5.3 nos ambientes mais comuns, especialmente em hosts compartilhados, o @ircmaxell criou uma camada de compatibilidade para a próxima API que é compatível com versões anteriores ao PHP 5.3.7.

Recapitulação e isenção de responsabilidade de criptografia

O poder computacional necessário para realmente quebrar uma senha com hash não existe. A única maneira de os computadores "violarem" uma senha é recriá-la e simular o algoritmo de hash usado para protegê-la. A velocidade do hash está linearmente relacionada à sua capacidade de ser forçada a bruto. Pior ainda, a maioria dos algoritmos de hash pode ser facilmente paralelizada para ter um desempenho ainda mais rápido. É por isso que esquemas caros como bcrypt e scrypt são tão importantes.

Você não pode prever todas as ameaças ou vias de ataque e, portanto, deve se esforçar ao máximo para proteger seus usuários com antecedência . Se não o fizer, pode até perder o fato de ter sido atacado até que seja tarde demais ... e você é responsável . Para evitar essa situação, aja de paranóico para começar. Ataque seu próprio software (internamente) e tente roubar credenciais do usuário, ou modificar as contas de outros usuários ou acessar seus dados. Se você não testar a segurança do seu sistema, não poderá culpar ninguém além de si mesmo.

Por fim: eu não sou um criptógrafo. Tudo o que eu disse é a minha opinião, mas acho que é baseado no bom e velho senso comum ... e muita leitura. Lembre-se, seja o mais paranóico possível, torne as coisas o mais difíceis de se intrometer possível e, em seguida, se você ainda estiver preocupado, entre em contato com um hacker de chapéu branco ou criptógrafo para ver o que eles dizem sobre seu código / sistema.

Robert K
fonte
9
um segredo não ajuda, pois seu banco de dados de senha deve ser secreto de qualquer maneira - se eles conseguirem se apossar desse banco de dados, também poderão encontrar qualquer segredo que você esteja usando. no entanto, é importante que o sal seja aleatório.
31735 frankodwyer
2
observe, não é realmente verdade que 'o poder computacional para descriptografar' ainda não exista. como a maioria das senhas são palavras de dicionário ou derivadas de dicionário, um ataque baseado em dicionário é geralmente muito eficaz (daí o uso de diretivas de senha e contagem de iterações).
Frankodwyer 31/12/08
6
@ pulga seca, eu não estou discutindo com você. Apenas apontando o quão complicada e complexa é essa área do nosso trabalho. Continuo na esperança de ser instruído pelas práticas recomendadas de tudo, de ponta a ponta, para configurar um pequeno sistema de gerenciamento de conteúdo de sites. Eu ainda estou aprendendo aqui. ... toda vez que leio algo que faz sentido, logo percebo 5 outros posts que o contradizem. que round-e-rodada fica vertiginosa rapidamente :)
m42
4
Revisão interessante. O ID do usuário (por exemplo, um auto incremento BIGINT) é um bom exemplo? Ou como não é aleatório, não é bom? Além disso, terei que armazenar o nonce para cada usuário no banco de dados ... A chave do site + nonce + HMAC fornece segurança aprimorada significativa sobre um hash salgado (com ID do usuário) iterado várias vezes? Da mesma forma, iterar o HMAC várias vezes é bom para segurança?
10898 Julian
4
Enviar uma senha temporária por e-mail que exija que o usuário a altere na primeira vez em que a usar e enviar um link "seguro" por e-mail que permita definir sua senha são igualmente arriscados. Nos dois casos, qualquer pessoa que intercepte o e-mail pode acessar a conta desde que use o link ou a senha antes do destinatário pretendido.
Tim Gautier
138

Uma resposta muito mais curta e segura - não escreva seu próprio mecanismo de senha , use um mecanismo testado e comprovado.

  • PHP 5.5 ou superior: password_hash () é de boa qualidade e faz parte do núcleo do PHP.
  • Versões antigas do PHP: a biblioteca phpass do OpenWall é muito melhor que a maioria dos códigos personalizados - usados ​​no WordPress, Drupal, etc.

A maioria dos programadores simplesmente não tem o conhecimento necessário para escrever códigos relacionados a criptografia com segurança, sem introduzir vulnerabilidades.

Autoteste rápido: o que é o alongamento de senha e quantas iterações você deve usar? Se você não souber a resposta, deve usar password_hash(), pois o alongamento de senha agora é um recurso crítico dos mecanismos de senha devido a CPUs muito mais rápidas e o uso de GPUs e FPGAs para quebrar senhas a taxas de bilhões de palpites por segundo (com GPUs )

Por exemplo, você pode decifrar todas as senhas do Windows de 8 caracteres em 6 horas usando 25 GPUs instaladas em 5 PCs de mesa. Isso é forçante, ou seja, enumerar e verificar cada senha do Windows de 8 caracteres , incluindo caracteres especiais, e não é um ataque de dicionário. Isso foi em 2012, a partir de 2018, você poderia usar menos GPUs ou quebrar mais rapidamente com 25 GPUs.

Também existem muitos ataques de tabelas do arco-íris às senhas do Windows que são executadas em CPUs comuns e são muito rápidas. Tudo isso ocorre porque o Windows ainda não modifica suas senhas, mesmo no Windows 10 - não cometa o mesmo erro da Microsoft!

Veja também:

  • excelente resposta com mais informações sobre o porquê password_hash()ou phpasso melhor caminho a percorrer.
  • bom artigo do blog que fornece 'fatores de trabalho' recomendados (número de iterações) para os principais algoritmos, incluindo bcrypt, scrypt e PBKDF2.
RichVel
fonte
1
mas esses sistemas são mais conhecidos e talvez já estejam comprometidos. mas é melhor do que você mesmo quando não sabe o que está fazendo.
JqueryToAddNumbers
14
Re "esses sistemas são mais conhecidos e talvez já estejam comprometidos" - não há razão para que um sistema bem projetado para autenticação se torne "já comprometido" apenas porque é mais conhecido. Bibliotecas como o phpass são escritas por especialistas e revisadas por muitas pessoas em detalhes - o fato de serem bem conhecidas acompanha uma revisão detalhada por diferentes pessoas e é mais provável que elas sejam seguras.
RichVel
Dado o recente hash dump de senhas do LinkedIn, Last.fm e outros, isso é bastante atual. Você está em boa companhia, sem saber como escrever seu próprio mecanismo de senha!
RichVel 5/12
3
@PP - as chances de um algoritmo de hash de senha revisado por pares ter um backdoor da NSA são muito baixas, na minha opinião. As chances de alguém que não é um especialista em criptografia real escrevendo um novo mecanismo de hash de senha sem outras vulnerabilidades são muito menores. E os usos webapp típicas apenas MD5 ou SHA-1 hash, que é terrível - mesmo de outra maneira grande Essencial livro PHP Segurança de Chris Shiflett recomenda MD5 ...
RichVel
1
@RichVel - A função password_hash (). Como mencionado anteriormente, ele é incorporado ao núcleo do PHP (também conhecido como / ext / standard).
CubicleSoft
43

Eu não armazenaria o hash da senha de duas maneiras diferentes, porque o sistema é pelo menos tão fraco quanto o mais fraco dos algoritmos de hash em uso.

Tom Haigh
fonte
não para hash de senha. o invasor precisa apenas quebrar um hash para recuperar a senha. de qualquer maneira, o ponto é discutível, pois nem o MD5 nem o SHA1 têm interrupções práticas disponíveis no cenário da senha.
frankodwyer 30/12/08
2
desculpe, interpretei errado sua resposta ao recomendar o uso de dois hashes ... na verdade, você está correto. O uso de dois hashes enfraquece o sistema no caso de senha, pois eles precisam apenas quebrar o hash mais fraco.
frankodwyer
40

A partir do PHP 5.5, o PHP possui funções simples e seguras para hash e verificação de senhas, password_hash () e password_verify ()

$password = 'anna';
$hash = password_hash($password, PASSWORD_DEFAULT);
$expensiveHash = password_hash($password, PASSWORD_DEFAULT, array('cost' => 20));

password_verify('anna', $hash); //Returns true
password_verify('anna', $expensiveHash); //Also returns true
password_verify('elsa', $hash); //Returns false

Quando password_hash()é usado, ele gera um sal aleatório e o inclui no hash emitido (junto com o custo e o algoritmo usado.), Em password_verify()seguida, lê esse hash e determina o método de criptografia e sal usado e o verifica com a senha de texto sem formatação fornecida.

Fornecendo o PASSWORD_DEFAULT instruções ao PHP para usar o algoritmo de hash padrão da versão instalada do PHP. Exatamente qual algoritmo significa mudar com o tempo em versões futuras, para que ele sempre seja um dos algoritmos mais fortes disponíveis.

O aumento do custo (cujo padrão é 10) dificulta a força bruta, mas também significa gerar hashes e verificar senhas contra eles, além de ser mais trabalhoso para a CPU do servidor.

Observe que, embora o algoritmo de hash padrão possa ser alterado, os hashes antigos continuarão a ser verificados, porque o algoritmo usado é armazenado no hash e o password_verify()pega.

AlliterativeAlice
fonte
33

Embora a pergunta tenha sido respondida, quero apenas reiterar que os sais usados ​​no hash devem ser aleatórios e não como o endereço de e-mail sugerido na primeira resposta.

Mais explicações estão disponíveis em http://www.pivotalsecurity.com/blog/password-hashing-salt-should-it-be-random/

Recentemente, discuti se os hashes de senha salgados com bits aleatórios são mais seguros do que aquele salgado com sais adivinhados ou conhecidos. Vamos ver: se o sistema que armazena a senha está comprometido, assim como o sistema que armazena o sal aleatório, o invasor terá acesso ao hash e ao salt, portanto, se o sal é aleatório ou não, não importa. O atacante pode gerar tabelas arco-íris pré-calculadas para quebrar o hash. Aí vem a parte interessante - não é tão trivial gerar tabelas pré-calculadas. Vamos dar um exemplo do modelo de segurança WPA. Sua senha WPA nunca é realmente enviada ao ponto de acesso sem fio. Em vez disso, ele é hash com seu SSID (o nome da rede, como Linksys, Dlink etc.). Uma explicação muito boa de como isso funciona está aqui. Para recuperar a senha do hash, você precisará saber a senha e o salt (nome da rede). A Church of Wifi já pré-calculou tabelas de hash com 1000 SSIDs principais e cerca de 1 milhão de senhas. O tamanho de todas as tabelas é de aproximadamente 40 GB. Como você pode ler em seu site, alguém usou 15 matrizes FGPA por 3 dias para gerar essas tabelas. Supondo que a vítima esteja usando o SSID como "a387csf3" e a senha como "123456", será quebrada por essas tabelas? Não! .. Eu não posso. Mesmo se a senha for fraca, as tabelas não têm hashes para o SSID a387csf3. Essa é a beleza de ter sal aleatório. Isso deterá os crackers que prosperam em tabelas pré-computadas. Pode parar um hacker determinado? Provavelmente não. Mas o uso de sais aleatórios fornece uma camada adicional de defesa. Enquanto estamos neste tópico, vamos discutir a vantagem adicional de armazenar sais aleatórios em um sistema separado. Cenário # 1: os hashes de senha são armazenados no sistema X e os valores de sal usados ​​para o hash são armazenados no sistema Y. Esses valores de sal são possíveis de adivinhar ou são conhecidos (por exemplo, nome de usuário) Cenário # 2: os hashes de senha são armazenados no sistema X e os valores de sal usados ​​para o hash é armazenado no sistema Y. Esses valores de sal são aleatórios. Caso o sistema X tenha sido comprometido, como você pode adivinhar, há uma enorme vantagem de usar o sal aleatório em um sistema separado (Cenário # 2). O atacante precisará adivinhar valores adicionais para poder quebrar hashes. Se um sal de 32 bits for usado, 2 ^ 32 = 4.294.967.296 (cerca de 4,2 bilhões) de iterações serão necessárias para cada senha adivinhada. Os hashes de senha são armazenados no sistema X e os valores de sal usados ​​para o hash são armazenados no sistema Y. Esses valores de sal são fáceis de adivinhar ou são conhecidos (por exemplo, nome de usuário) Cenário 2: Os hashes de senha são armazenados no sistema X e os valores de sal usados ​​para o hash são armazenados no sistema Y sistema Y. Esses valores de sal são aleatórios. Caso o sistema X tenha sido comprometido, como você pode adivinhar, há uma enorme vantagem de usar o sal aleatório em um sistema separado (Cenário # 2). O atacante precisará adivinhar valores adicionais para poder quebrar hashes. Se um sal de 32 bits for usado, 2 ^ 32 = 4.294.967.296 (cerca de 4,2 bilhões) de iterações serão necessárias para cada senha adivinhada. Os hashes de senha são armazenados no sistema X e os valores de sal usados ​​para o hash são armazenados no sistema Y. Esses valores de sal são fáceis de adivinhar ou são conhecidos (por exemplo, nome de usuário) Cenário 2: Os hashes de senha são armazenados no sistema X e os valores de sal usados ​​para o hash são armazenados no sistema Y sistema Y. Esses valores de sal são aleatórios. Caso o sistema X tenha sido comprometido, como você pode adivinhar, há uma enorme vantagem de usar o sal aleatório em um sistema separado (Cenário # 2). O atacante precisará adivinhar valores adicionais para poder quebrar hashes. Se um sal de 32 bits for usado, 2 ^ 32 = 4.294.967.296 (cerca de 4,2 bilhões) de iterações serão necessárias para cada senha adivinhada. Esses valores de sal são aleatórios. Caso o sistema X tenha sido comprometido, como você pode adivinhar, há uma enorme vantagem de usar o sal aleatório em um sistema separado (Cenário # 2). O atacante precisará adivinhar valores adicionais para poder quebrar hashes. Se um sal de 32 bits for usado, 2 ^ 32 = 4.294.967.296 (cerca de 4,2 bilhões) de iterações serão necessárias para cada senha adivinhada. Esses valores de sal são aleatórios. Caso o sistema X tenha sido comprometido, como você pode adivinhar, há uma enorme vantagem de usar o sal aleatório em um sistema separado (Cenário # 2). O atacante precisará adivinhar valores adicionais para poder quebrar hashes. Se um sal de 32 bits for usado, 2 ^ 32 = 4.294.967.296 (cerca de 4,2 bilhões) de iterações serão necessárias para cada senha adivinhada.

Gaurav Kumar
fonte
7
Mesmo que o atacante consiga se livrar, uma string "sitesalt: usersalt: password" ainda é resistente a tabelas pré-computadas, pois o invasor precisa gerar as tabelas para todos os usuários (para que o ataque se torne muito mais lento), a menos que um usuário específico está sendo alvo ...
luiscubal
Em relação a "Mesmo que o invasor receba o sal, uma string" sitesalt: usersalt: password "ainda é resistente a tabelas pré-computadas", concordo totalmente. O que quero dizer é que o sitesalt, se tornado aleatório e longo, tornará o sistema mais seguro do que previsível (sitesalt). Eu já vi algumas pessoas recomendando o uso de ID de e-mail etc. como sal, e eu o desencorajo.
Gaurav Kumar
Você perdeu o que eu escrevi originalmente. Eu disse para usar um nonce aleatório, armazenado com o registro, MAIS o endereço de e-mail. A adição do endereço de email cria uma entropia extra para o hacker trabalhar. Desde então, reescrevi minha resposta em favor do bcrypt.
Robert K
28

Eu só quero ressaltar que o PHP 5.5 inclui uma API de hash de senha que fornece um invólucro crypt(). Essa API simplifica significativamente a tarefa de hash, verificação e rehashing de hashes de senha. O autor também lançou um pacote de compatibilidade (na forma de um único arquivo password.php que você simplesmente requireusa), para aqueles que usam o PHP 5.3.7 e posterior e desejam usá-lo agora.

Ele suporta apenas o BCRYPT por enquanto, mas pretende ser facilmente estendido para incluir outras técnicas de hash de senha e, como a técnica e o custo são armazenados como parte do hash, as alterações na sua técnica / custo preferencial não invalidarão os hashes atuais. automagicamente, use a técnica / custo corretos ao validar. Ele também controla a geração de um sal "seguro" se você não definir explicitamente o seu próprio.

A API expõe quatro funções:

  • password_get_info() - retorna informações sobre o hash especificado
  • password_hash() - cria um hash de senha
  • password_needs_rehash()- verifica se o hash fornecido corresponde às opções fornecidas. Útil para verificar se o hash está em conformidade com o seu esquema atual de técnica / custo, permitindo que você refaça o procedimento se necessário
  • password_verify() - verifica se uma senha corresponde a um hash

No momento, essas funções aceitam as constantes de senha PASSWORD_BCRYPT e PASSWORD_DEFAULT, que são sinônimos no momento, a diferença é que PASSWORD_DEFAULT "pode ​​mudar nas versões mais recentes do PHP quando novos algoritmos de hash mais fortes são suportados". O uso de PASSWORD_DEFAULT e password_needs_rehash () no logon (e rehashing se necessário) deve garantir que seus hashes sejam razoavelmente resistentes a ataques de força bruta com pouco ou nenhum trabalho para você.

Edição: Acabei de perceber que isso é mencionado brevemente na resposta de Robert K. Deixarei essa resposta aqui, pois acho que fornece um pouco mais de informações sobre como funciona e a facilidade de uso que fornece para quem não conhece a segurança.

JonoCoetzee
fonte
19

Estou usando o Phpass, que é uma classe PHP de um arquivo simples que pode ser implementada com muita facilidade em quase todos os projetos PHP. Ver também O H .

Por padrão, ele usava a criptografia disponível mais forte, implementada no Phpass, que bcryptremonta a outras criptografias no MD5 para fornecer compatibilidade com versões anteriores para estruturas como o Wordpress.

O hash retornado pode ser armazenado no banco de dados como está. O uso de amostra para gerar hash é:

$t_hasher = new PasswordHash(8, FALSE);
$hash = $t_hasher->HashPassword($password);

Para verificar a senha, pode-se usar:

$t_hasher = new PasswordHash(8, FALSE);
$check = $t_hasher->CheckPassword($password, $hash);
rabudde
fonte
14

COISAS PARA LEMBRAR

Muito foi dito sobre a criptografia de senha para PHP, a maioria das quais é um conselho muito bom, mas antes mesmo de iniciar o processo de uso do PHP para criptografia de senha, verifique se você tem o seguinte implementado ou pronto para ser implementado.

SERVIDOR

PORTOS

Não importa quão boa seja sua criptografia, se você não proteger adequadamente o servidor que executa o PHP e o DB, todos os seus esforços serão inúteis. A maioria dos servidores funciona relativamente da mesma maneira, eles têm portas designadas para permitir que você os acesse remotamente através de ftp ou shell. Certifique-se de alterar a porta padrão em que a conexão remota está ativa. Ao não fazer isso, você efetivamente fez com que o invasor fizesse uma etapa a menos no acesso ao seu sistema.

NOME DO USUÁRIO

Por tudo que é bom no mundo, não use o nome de usuário admin, root ou algo semelhante. Além disso, se você estiver em um sistema baseado em unix, NÃO torne o login da conta raiz acessível, ele deve sempre ser apenas sudo.

SENHA

Você diz aos usuários para criar boas senhas para evitar serem invadidos, faça o mesmo. Qual é o sentido de passar por todo o esforço de trancar a porta da frente quando você tem a porta dos fundos aberta?

BASE DE DADOS

SERVIDOR

Idealmente, você deseja seu DB e APPLICATION em servidores separados. Isso nem sempre é possível devido ao custo, mas permite alguma segurança, pois o invasor precisará executar duas etapas para acessar totalmente o sistema.

DO UTILIZADOR

Sempre faça com que seu aplicativo tenha sua própria conta para acessar o banco de dados e dê apenas os privilégios necessários.

Em seguida, tenha uma conta de usuário separada para você que não esteja armazenada em nenhum lugar do servidor, nem mesmo no aplicativo.

Como sempre, NÃO crie essa raiz ou algo semelhante.

SENHA

Siga as mesmas diretrizes de todas as boas senhas. Além disso, não reutilize a mesma senha em nenhuma conta SERVER ou DB no mesmo sistema.

PHP

SENHA

NUNCA armazene uma senha no seu banco de dados, em vez disso, armazene o hash e o sal exclusivo, explicarei por que mais tarde.

HASHING

HASHING DE UMA MANEIRA !!!!!!!, Nunca use uma senha com hash de uma maneira que possa ser revertida; os hashes devem ser de uma maneira, o que significa que você não os reverterá e os comparará com a senha, mas sim a senha digitada. da mesma maneira e compare os dois hashes. Isso significa que, mesmo que um invasor obtenha acesso ao banco de dados, ele não sabe qual é realmente a senha, apenas o hash resultante. O que significa mais segurança para seus usuários no pior cenário possível.

Há um monte de bons funções hash lá fora ( password_hash, hash, etc ...), mas você precisa selecionar um bom algoritmo para o hash para ser eficaz. (bcrypt e similares a ele são algoritmos decentes).

Quando a velocidade do hash é a chave, quanto mais lento, mais resistente aos ataques da Força Bruta.

Um dos erros mais comuns no hash é que os hashes não são exclusivos dos usuários. Isso ocorre principalmente porque os sais não são gerados exclusivamente.

SALGA

As senhas sempre devem ser salgadas antes do hash. Salting adiciona uma sequência aleatória à senha para que senhas semelhantes não apareçam iguais no banco de dados. No entanto, se o sal não for exclusivo de cada usuário (ou seja: você usa um sal codificado), você praticamente o tornará inútil. Porque uma vez que um invasor descobre um sal de senha, ele tem sal para todos eles.

Ao criar um salt, verifique se ele é exclusivo da senha que está salgando e, em seguida, armazene o hash e o sal completos no seu DB. O que isso fará é fazer com que um invasor precise quebrar individualmente cada sal e hash antes de obter acesso. Isso significa muito mais trabalho e tempo para o invasor.

USUÁRIOS QUE CRIAM SENHAS

Se o usuário estiver criando uma senha pelo frontend, isso significa que ela deve ser enviada ao servidor. Isso abre um problema de segurança, porque isso significa que a senha não criptografada está sendo enviada para o servidor e, se um invasor puder ouvir e acessar, toda a sua segurança no PHP é inútil. SEMPRE transmita os dados com SEGURANÇA, isso é feito via SSL, mas fique cansado, mesmo o SSL não é perfeito (a falha Heartbleed do OpenSSL é um exemplo disso).

Também faça o usuário criar uma senha segura, é simples e sempre deve ser feita, o usuário será grato por isso no final.

Por fim, independentemente das medidas de segurança adotadas, 100% de segurança, quanto mais avançada a tecnologia a ser protegida, mais avançados os ataques. Mas seguir estas etapas tornará seu site mais seguro e muito menos desejável para os invasores.

Aqui está uma classe PHP que cria um hash e salt para uma senha facilmente

http://git.io/mSJqpw

wmfrancia
fonte
1
Você deve encontrar o SHA512 em sua lista de algoritmos de hash decentes, porque é muito rápido. Use-o apenas em combinação com PBKDF2. Enquanto o BCrypt é baseado em blowfish, o próprio blowfish é um algoritmo para criptografia, não para hash.
martinstoeckli
1
Como você armazena o sal aleatório no DB? Acho que você não faz o hash (não pode ser usado para verificação) nem o armazena de maneira clara (sem benefícios reais se o invasor puder ler o banco de dados). Então, como você faz isso?
Iazel
wmfrancia escreveu: "Salting adiciona uma string aleatória à senha para que senhas semelhantes não apareçam iguais no banco de dados". Isto não faz sentido para mim. Hashes no banco de dados já parecerão diferentes, porque essa é uma propriedade das funções de hash.
H2ONaCl 26/02
wmfancia escreveu a respeito de um sal constante: "uma vez que um atacante descobre um sal de senha, ele tem sal para todos eles". O mesmo pode ser dito que, se o hacker descobrir qual campo do DB é o sal, ele terá os sais para todos eles. Como um sal constante provavelmente não estaria no DB, isso é bom em um sal constante.
H2ONaCl 26/02
Obviamente, esses comentários não devem sugerir que um sal aleatório por usuário não seja melhor que um sal por aplicativo. É melhor.
H2ONaCl 26/02
12

O Google diz que o SHA256 está disponível para PHP.

Você definitivamente deveria usar sal. Eu recomendo o uso de bytes aleatórios (e não se limite a caracteres e números). Como de costume, quanto mais tempo você escolher, mais seguro e mais lento fica. 64 bytes devem estar bem, eu acho.

AticusFinch
fonte
13
64 bits deve ser suficiente para alguém?
precisa saber é o seguinte
@ Konerak, eu voltaria a isso depois de 20 anos. :) Mas sim o SHA256 está realmente disponível. Se você quiser saber como seguro SHA256 é, você pode querer verificar isso: security.stackexchange.com/questions/90064/...
Vincent Edward Gedaria Binua
8

No final, o hash duplo, matematicamente, não oferece nenhum benefício. Na prática, no entanto, é útil para impedir ataques baseados em tabelas do arco-íris. Em outras palavras, não é mais benéfico do que fazer hash com sal, o que leva muito menos tempo do processador em seu aplicativo ou em seu servidor.

Máx.
fonte
2
O hash múltiplo também protege contra ataques de dicionário e força bruta - isto é, simplesmente leva mais tempo para serem computados.
Frankodwyer
6
o hash duplo não oferece uma vantagem significativa, mas as iterações de hash multi-rodada ainda são uma defesa viável contra ataques de força de dicionário e bruce. Os hashes de senha de força industrial usam mais de 1000 rodadas. O PBKDF1 da PKCS # 5 sugere no mínimo 1000 rodadas.
22130 Berk D. Demir
8

Encontrei o tópico perfeito sobre esse assunto aqui: https://crackstation.net/hashing-security.htm , queria que você se beneficiasse, aqui está o código-fonte que também previa contra ataques baseados em tempo.

<?php
/*
 * Password hashing with PBKDF2.
 * Author: havoc AT defuse.ca
 * www: https://defuse.ca/php-pbkdf2.htm
 */

// These constants may be changed without breaking existing hashes.
define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 1000);
define("PBKDF2_SALT_BYTES", 24);
define("PBKDF2_HASH_BYTES", 24);

define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);

function create_hash($password)
{
    // format: algorithm:iterations:salt:hash
    $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
    return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" . 
        base64_encode(pbkdf2(
            PBKDF2_HASH_ALGORITHM,
            $password,
            $salt,
            PBKDF2_ITERATIONS,
            PBKDF2_HASH_BYTES,
            true
        ));
}

function validate_password($password, $good_hash)
{
    $params = explode(":", $good_hash);
    if(count($params) < HASH_SECTIONS)
       return false; 
    $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
    return slow_equals(
        $pbkdf2,
        pbkdf2(
            $params[HASH_ALGORITHM_INDEX],
            $password,
            $params[HASH_SALT_INDEX],
            (int)$params[HASH_ITERATION_INDEX],
            strlen($pbkdf2),
            true
        )
    );
}

// Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
    $diff = strlen($a) ^ strlen($b);
    for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
    {
        $diff |= ord($a[$i]) ^ ord($b[$i]);
    }
    return $diff === 0; 
}

/*
 * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
 * $algorithm - The hash algorithm to use. Recommended: SHA256
 * $password - The password.
 * $salt - A salt that is unique to the password.
 * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
 * $key_length - The length of the derived key in bytes.
 * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
 * Returns: A $key_length-byte key derived from the password and salt.
 *
 * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
 *
 * This implementation of PBKDF2 was originally created by https://defuse.ca
 * With improvements by http://www.variations-of-shadow.com
 */
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
    $algorithm = strtolower($algorithm);
    if(!in_array($algorithm, hash_algos(), true))
        die('PBKDF2 ERROR: Invalid hash algorithm.');
    if($count <= 0 || $key_length <= 0)
        die('PBKDF2 ERROR: Invalid parameters.');

    $hash_length = strlen(hash($algorithm, "", true));
    $block_count = ceil($key_length / $hash_length);

    $output = "";
    for($i = 1; $i <= $block_count; $i++) {
        // $i encoded as 4 bytes, big endian.
        $last = $salt . pack("N", $i);
        // first iteration
        $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
        // perform the other $count - 1 iterations
        for ($j = 1; $j < $count; $j++) {
            $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
        }
        $output .= $xorsum;
    }

    if($raw_output)
        return substr($output, 0, $key_length);
    else
        return bin2hex(substr($output, 0, $key_length));
}
?>
Jason OOO
fonte
Você nos dá uma solução sem uso, sem uso #
Michael
6

Normalmente, uso SHA1 e salt com o ID do usuário (ou alguma outra informação específica do usuário) e, às vezes, uso adicionalmente um sal constante (por isso tenho 2 partes para o sal).

O SHA1 agora também é considerado um pouco comprometido, mas em um grau muito menor que o MD5. Ao usar um sal (qualquer sal), você está impedindo o uso de uma tabela arco-íris genérica para atacar seus hashes (algumas pessoas tiveram sucesso usando o Google como uma espécie de tabela arco-íris pesquisando o hash). É possível que um invasor gere uma tabela de arco-íris usando o seu salt; é por isso que você deve incluir um sal específico do usuário. Dessa forma, eles terão que gerar uma tabela arco-íris para todos os registros do sistema, não apenas uma para todo o sistema! Com esse tipo de salga, até o MD5 é decentemente seguro.

rmeador
fonte
2
sal constante não é uma ótima idéia ... provavelmente não é uma falha fatal, mas enfraquece desnecessariamente o esquema.
frankodwyer 30/12/08
O MD5 e o SHA1 são rápidos, portanto, essa é uma diéia ruim.
CodesInChaos
4

SHA1 e um sal devem ser suficientes (dependendo, naturalmente, se você está codificando algo para Fort Knox ou um sistema de login para sua lista de compras) no futuro próximo. Se o SHA1 não for bom o suficiente para você, use o SHA256 .

A idéia de um sal é desequilibrar os resultados do hash, por assim dizer. Sabe-se, por exemplo, que o hash MD5 de uma sequência vazia é d41d8cd98f00b204e9800998ecf8427e. Portanto, se alguém com uma memória boa o suficiente ver esse hash e saber que é o hash de uma string vazia. Mas se a string for salgada (digamos, com a string " MY_PERSONAL_SALT"), o hash da 'string vazia' (ou seja, " MY_PERSONAL_SALT") se tornará aeac2612626724592271634fb14d3ea6, portanto, não óbvio para voltar atrás. O que eu estou tentando dizer, que é melhor usar qualquer sal, do que não. Portanto, não é muito importante saber qual sal usar.

Na verdade, existem sites que fazem exatamente isso - você pode alimentá-lo com um hash (md5) e ele gera um texto simples conhecido que gera esse hash específico. Se você obtivesse acesso a um banco de dados que armazena hash md5 simples, seria fácil inserir o hash do administrador para esse serviço e efetuar login. Mas, se as senhas fossem salgadas, esse serviço se tornaria ineficaz.

Além disso, o hash duplo é geralmente considerado um método ruim, porque diminui o espaço de resultado. Todos os hashes populares são de comprimento fixo. Assim, você pode ter apenas valores finitos desse comprimento fixo e os resultados se tornam menos variados. Isso pode ser considerado como outra forma de salga, mas eu não recomendaria.

Henrik Paul
fonte
O site de destino não deve conter nada muito sensível (não é um banco), mas ainda assim prefiro protegê-lo.
30568 luiscubal
1
hash duplo não reduz o espaço de resultado. O hash iterativo é um controle comum contra ataques de dicionário e força bruta (diminui muito mais o desempenho do que a verificação de senha).
frankodwyer
2
@ frankodwyer: sim, é ruim. sha1(sha1($foo))reduz efetivamente o espaço de saída, porque qualquer colisão da função interna se tornará automaticamente uma colisão da externa. A degradação é linear, mas ainda é uma preocupação. Os métodos iterativos de hash retornam dados a cada rodada, como $hash = sha1(sha1($salt . $password) . $salt). Mas isso ainda não é bom ... Stick with PBKDF2 ou Bcrypt ...
ircmaxell
-7

ok no instável precisamos de sal, sal deve ser único, então vamos gerá-lo

   /**
     * Generating string
     * @param $size
     * @return string
     */
    function Uniwur_string($size){
        $text = md5(uniqid(rand(), TRUE));
        RETURN substr($text, 0, $size);
    }

também precisamos do hash que estou usando sha512 é o melhor e está em php

   /**
     * Hashing string
     * @param $string
     * @return string
     */
    function hash($string){
        return hash('sha512', $string);
    }

agora podemos usar essas funções para gerar senha segura

// generating unique password
$password = Uniwur_string(20); // or you can add manual password
// generating 32 character salt
$salt = Uniwur_string(32);
// now we can manipulate this informations

// hashin salt for safe
$hash_salt = hash($salt);
// hashing password
$hash_psw = hash($password.$hash_salt);

agora precisamos salvar no banco de dados nosso valor variável $ hash_psw e variável $ salt

e para autorizar, usaremos as mesmas etapas ...

é a melhor maneira de proteger as senhas de nossos clientes ...

Ps nos últimos 2 passos, você pode usar seu próprio algoritmo ... mas certifique-se de poder gerar essa senha com hash no futuro quando precisar autorizar o usuário ...

shalvasoft
fonte
4
Esta pergunta foi sobre hashes para senhas. 1 execução de sha512(mesmo que salgada) é amplamente considerada como insuficiente para proteção de senha. (também que o RNG não é criptograficamente seguro, portanto, usá-lo para geração de senha é arriscado).
luiscubal
2
Você não tem ideia do que está fazendo. Leia as principais respostas desta postagem e veja por que seu código não é apenas inseguro, mas não faz sentido.
Cryptic #
Está bem. meu código não é seguro. então deixe-me saber por que você está usando em seus algoritmos ony sha256 ??? Eu sei que sha512 é o melhor porque não usá-lo ???
shalvasoft
1
O @shalvasoft sha512 é muito bom para o hash de uso geral, mas a proteção por senha requer hashes com propriedades muito específicas ("ser lento" é estranhamente uma coisa boa , por exemplo, e o sha512 é muito rápido). Algumas pessoas usaram o sha512 como um bloco de construção para criar funções de hash de senha, mas hoje em dia a abordagem recomendada é "use bcrypt e fique de olho no scrypt".
11385 luiscubal