Como você usa bcrypt para hash de senhas em PHP?


1256

De vez em quando, ouço o conselho "Use o bcrypt para armazenar senhas no PHP, regras do bcrypt".

Mas o que é isso bcrypt? O PHP não oferece essas funções, a Wikipedia fala sobre um utilitário de criptografia de arquivos e as pesquisas na Web apenas revelam algumas implementações do Blowfish em diferentes idiomas. Agora, o Blowfish também está disponível em PHP mcrypt, mas como isso ajuda no armazenamento de senhas? Blowfish é uma cifra de uso geral, funciona de duas maneiras. Se puder ser criptografado, poderá ser descriptografado. As senhas precisam de uma função de hash unidirecional.

Qual a explicação?


13
Esta questão foi abordada anteriormente , e sua sugestão de usar uma biblioteca padrão é excelente. A segurança é uma questão complicada e, usando um pacote projetado por alguém que sabe o que diabos eles estão fazendo, você está apenas se ajudando.
eykanal

59
@eykanal - essa página nem menciona bcrypt, muito menos explica o que é .
Vilx-

8
@eykanal - Não pergunto como funciona. Eu só quero saber o que é isso. Porque tudo o que posso descobrir na rede sob a palavra-chave "bcrypt" não pode ser usado de maneira nenhuma para hash de senhas. Não diretamente, de qualquer maneira, e não em PHP. OK, agora eu entendo que é realmente o pacote "phpass" que usa blowfish para criptografar sua senha com uma chave derivada da sua senha (em essência, criptografar a senha consigo). Mas referenciá-lo como "bcrypt" é muito enganador, e é isso que eu queria esclarecer nesta pergunta.
Vilx-

3
@ Vilx: adicionei mais informações sobre o porquê de bcryptum algoritmo de hash unidirecional versus um esquema de criptografia na minha resposta . Existe todo esse equívoco que bcrypté apenas o Blowfish quando, na verdade, ele possui uma programação de teclas totalmente diferente que garante que o texto simples não possa ser recuperado do texto cifrado sem conhecer o estado inicial da cifra (salt, rounds, key).
Andrew Moore

1
Veja também a estrutura de hash de senha PHP portátil do Openwall (PHPass). É reforçado contra uma série de ataques comuns a senhas de usuários.
JWW

Respostas:


1065

bcrypté um algoritmo de hash escalável com hardware (através de um número configurável de rodadas). Sua lentidão e várias rodadas garantem que um invasor precise implantar fundos e hardware maciços para poder decifrar suas senhas. Adicione a isso os sais por senha ( bcryptEXIGE sais) e você pode ter certeza de que um ataque é praticamente inviável sem uma quantidade absurda de fundos ou hardware.

bcryptusa o algoritmo Eksblowfish para hash senhas. Embora a fase de criptografia do Eksblowfish e do Blowfish seja exatamente a mesma, a fase de programação principal do Eksblowfish garante que qualquer estado subsequente dependa do sal e da chave (senha do usuário), e nenhum estado pode ser pré-computado sem o conhecimento de ambos. Devido a essa diferença fundamental, bcrypté um algoritmo de hash unidirecional. Você não pode recuperar a senha em texto simples sem conhecer o salt, rounds e key (senha). [ Fonte ]

Como usar o bcrypt:

Usando PHP> = 5.5-DEV

As funções de hash de senha agora foram construídas diretamente no PHP> = 5.5 . Agora você pode usar password_hash()para criar um bcrypthash de qualquer senha:

<?php
// Usage 1:
echo password_hash('rasmuslerdorf', PASSWORD_DEFAULT)."\n";
// $2y$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// For example:
// $2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a

// Usage 2:
$options = [
  'cost' => 11
];
echo password_hash('rasmuslerdorf', PASSWORD_BCRYPT, $options)."\n";
// $2y$11$6DP.V0nO7YI3iSki4qog6OQI5eiO6Jnjsqg7vdnb.JgGIsxniOn4C

Para verificar uma senha fornecida pelo usuário com relação a um hash existente, você pode usar o seguinte password_verify():

<?php
// See the password_hash() example to see where this came from.
$hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';

if (password_verify('rasmuslerdorf', $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}

Usando PHP> = 5.3.7, <5.5-DEV (também RedHat PHP> = 5.3.3)

Existe uma biblioteca de compatibilidade no GitHub criada com base no código fonte das funções acima, originalmente escritas em C, que fornece a mesma funcionalidade. Depois que a biblioteca de compatibilidade é instalada, o uso é o mesmo que acima (menos a notação abreviada de matriz, se você ainda estiver na ramificação 5.3.x).

Usando PHP <5.3.7 (DEPRECATED)

Você pode usar a crypt()função para gerar hashes bcrypt de strings de entrada. Essa classe pode gerar sais automaticamente e verificar os hashes existentes em uma entrada. Se você estiver usando uma versão do PHP maior ou igual a 5.3.7, é altamente recomendável usar a função interna ou a biblioteca compat . Essa alternativa é fornecida apenas para fins históricos.

class Bcrypt{
  private $rounds;

  public function __construct($rounds = 12) {
    if (CRYPT_BLOWFISH != 1) {
      throw new Exception("bcrypt not supported in this installation. See http://php.net/crypt");
    }

    $this->rounds = $rounds;
  }

  public function hash($input){
    $hash = crypt($input, $this->getSalt());

    if (strlen($hash) > 13)
      return $hash;

    return false;
  }

  public function verify($input, $existingHash){
    $hash = crypt($input, $existingHash);

    return $hash === $existingHash;
  }

  private function getSalt(){
    $salt = sprintf('$2a$%02d$', $this->rounds);

    $bytes = $this->getRandomBytes(16);

    $salt .= $this->encodeBytes($bytes);

    return $salt;
  }

  private $randomState;
  private function getRandomBytes($count){
    $bytes = '';

    if (function_exists('openssl_random_pseudo_bytes') &&
        (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) { // OpenSSL is slow on Windows
      $bytes = openssl_random_pseudo_bytes($count);
    }

    if ($bytes === '' && is_readable('/dev/urandom') &&
       ($hRand = @fopen('/dev/urandom', 'rb')) !== FALSE) {
      $bytes = fread($hRand, $count);
      fclose($hRand);
    }

    if (strlen($bytes) < $count) {
      $bytes = '';

      if ($this->randomState === null) {
        $this->randomState = microtime();
        if (function_exists('getmypid')) {
          $this->randomState .= getmypid();
        }
      }

      for ($i = 0; $i < $count; $i += 16) {
        $this->randomState = md5(microtime() . $this->randomState);

        if (PHP_VERSION >= '5') {
          $bytes .= md5($this->randomState, true);
        } else {
          $bytes .= pack('H*', md5($this->randomState));
        }
      }

      $bytes = substr($bytes, 0, $count);
    }

    return $bytes;
  }

  private function encodeBytes($input){
    // The following is code from the PHP Password Hashing Framework
    $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

    $output = '';
    $i = 0;
    do {
      $c1 = ord($input[$i++]);
      $output .= $itoa64[$c1 >> 2];
      $c1 = ($c1 & 0x03) << 4;
      if ($i >= 16) {
        $output .= $itoa64[$c1];
        break;
      }

      $c2 = ord($input[$i++]);
      $c1 |= $c2 >> 4;
      $output .= $itoa64[$c1];
      $c1 = ($c2 & 0x0f) << 2;

      $c2 = ord($input[$i++]);
      $c1 |= $c2 >> 6;
      $output .= $itoa64[$c1];
      $output .= $itoa64[$c2 & 0x3f];
    } while (true);

    return $output;
  }
}

Você pode usar este código assim:

$bcrypt = new Bcrypt(15);

$hash = $bcrypt->hash('password');
$isGood = $bcrypt->verify('password', $hash);

Como alternativa, você também pode usar o Portable PHP Hashing Framework .


7
@ The Wicked Flea: Desculpe desapontá-lo, mas mt_rand()também é transmitido usando o horário atual e o ID do processo atual. Por favor, veja GENERATE_SEED()em/ext/standard/php_rand.h .
Andrew Moore

53
@ Mike: Vá em frente, está lá exatamente por esse motivo!
Andrew Moore

14
Para quem pensa que precisa modificar o início da string $ salt na função getSalt, isso não é necessário. O $ 2a $ __ faz parte do sal CRYPT_BLOWFISH. Na documentação: "Blowfish mistura com sal da seguinte forma:" $ 2a $ ", um parâmetro de custo de dois dígitos," $ "e 22 dígitos do alfabeto".
jwinn

18
@ MichaelLang: A coisa boa crypt()é revisada por pares e verificada então. O código acima chama PHP crypt(), que chama a crypt()função POSIX . Todo o código acima faz mais ao gerar um sal aleatório (que não precisa ser criptograficamente seguro, o sal não é considerado um segredo) antes de chamar crypt(). Talvez você deva fazer uma pequena pesquisa antes de ligar para o lobo.
Andrew Moore

31
Observe que essa resposta, embora boa, está começando a mostrar sua idade. Este código (como qualquer implementação PHP que se baseia crypt()) está sujeito a uma vulnerabilidade de segurança anterior à 5.3.7 e é (levemente) ineficiente após a 5.3.7 - detalhes do problema relevante podem ser encontrados aqui . Observe também que a nova API de hash de senha ( retrocompat lib ) agora é o método preferido para implementar o hash de senha bcrypt no seu aplicativo.
DaveRandom

295

Então, você quer usar o bcrypt? Impressionante! No entanto, como outras áreas da criptografia, você não deve fazer isso sozinho. Se você precisa se preocupar com algo como gerenciar chaves, armazenar sais ou gerar números aleatórios, está fazendo algo errado.

O motivo é simples: é tão trivialmente fácil estragar o bcrypt . De fato, se você examinar quase todos os trechos de código desta página, notará que está violando pelo menos um desses problemas comuns.

Enfrente, a criptografia é difícil.

Deixe para os especialistas. Deixe para as pessoas cujo trabalho é manter essas bibliotecas. Se você precisa tomar uma decisão, está fazendo algo errado.

Em vez disso, basta usar uma biblioteca. Existem vários, dependendo dos seus requisitos.

Bibliotecas

Aqui está um detalhamento de algumas das APIs mais comuns.

API do PHP 5.5 - (disponível para 5.3.7 ou superior)

A partir do PHP 5.5, uma nova API para hash de senhas está sendo introduzida. Também há uma biblioteca de compatibilidade de calços mantida (por mim) para 5.3.7+. Isso tem o benefício de ser uma implementação revisada por pares e simples de usar.

function register($username, $password) {
    $hash = password_hash($password, PASSWORD_BCRYPT);
    save($username, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    if (password_verify($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Realmente, o objetivo é ser extremamente simples.

Recursos:

Zend \ Crypt \ Password \ Bcrypt (5.3.2 ou superior)

Essa é outra API semelhante à do PHP 5.5 e tem um objetivo semelhante.

function register($username, $password) {
    $bcrypt = new Zend\Crypt\Password\Bcrypt();
    $hash = $bcrypt->create($password);
    save($user, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    $bcrypt = new Zend\Crypt\Password\Bcrypt();
    if ($bcrypt->verify($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Recursos:

PasswordLib

Essa é uma abordagem ligeiramente diferente para o hash de senha. Em vez de simplesmente dar suporte ao bcrypt, o PasswordLib suporta um grande número de algoritmos de hash. É principalmente útil em contextos em que você precisa oferecer suporte à compatibilidade com sistemas legados e diferentes que podem estar fora de seu controle. Ele suporta um grande número de algoritmos de hash. E é suportado 5.3.2+

function register($username, $password) {
    $lib = new PasswordLib\PasswordLib();
    $hash = $lib->createPasswordHash($password, '$2y$', array('cost' => 12));
    save($user, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    $lib = new PasswordLib\PasswordLib();
    if ($lib->verifyPasswordHash($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Referências:

  • Código-fonte / Documentação: GitHub

PHPASS

Esta é uma camada que suporta bcrypt, mas também suporta um algoritmo bastante forte que é útil se você não tiver acesso ao PHP> = 5.3.2 ... Na verdade, ele suporta o PHP 3.0+ (embora não com o bcrypt).

function register($username, $password) {
    $phpass = new PasswordHash(12, false);
    $hash = $phpass->HashPassword($password);
    save($user, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    $phpass = new PasswordHash(12, false);
    if ($phpass->CheckPassword($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Recursos

Nota: Não use as alternativas PHPASS que não estão hospedadas no openwall, pois são projetos diferentes !!!

Sobre o BCrypt

Se você notar, cada uma dessas bibliotecas retorna uma única sequência. É por isso que o BCrypt funciona internamente. E há uma tonelada de respostas sobre isso. Aqui está uma seleção que eu escrevi, que não vou copiar / colar aqui, mas link para:

Embrulhar

Existem muitas opções diferentes. A escolha que você escolher depende de você. No entanto, gostaria ALTAMENTE recomendamos que você use uma das bibliotecas acima para lidar com isso para você.

Novamente, se você estiver usando crypt()diretamente, provavelmente está fazendo algo errado. Se o seu código estiver usando hash()(ou md5()ousha1() ) diretamente, você está quase definitivamente fazendo algo errado.

Basta usar uma biblioteca ...


7
O sal deve ser gerado aleatoriamente, no entanto, não precisa vir de uma fonte aleatória segura. O sal não é um segredo . Ser capaz de adivinhar o próximo sal não tem impacto real na segurança; Contanto que eles venham de um conjunto de dados suficientemente grande para gerar sais diferentes para cada senha codificada, você estará bem. Lembre-se de que o sal existe para impedir o uso de mesas arco-íris se seus hashes estiverem em mãos ruins. Eles não são secretos.
Andrew Moore

7
@AndrewMoore absolutamente correto! No entanto, o sal precisa ter entropia suficiente para ser estatisticamente único. Não apenas no seu aplicativo, mas em todos os aplicativos. Portanto, mt_rand()tem um período suficientemente alto, mas o valor inicial é de apenas 32 bits. Portanto, o uso mt_rand()efetivamente limita você a apenas 32 bits de entropia. O que, graças ao Problema do aniversário, significa que você tem 50% de chance de colisão com apenas 7k de sais gerados (globalmente). Como bcryptaceita 128 bits de sal, é melhor usar uma fonte que possa fornecer todos os 128 bits ;-). (em 128 bits, 50% de chance de colisão acontece no 2e19 hashes) ...
ircmaxell

1
@ircmaxell: Hense o "conjunto de dados suficientemente grande". No entanto, sua fonte não precisa ser uma fonte de entropia MUITO ALTA, apenas alta o suficiente para os 128 bits. No entanto, se você esgotou todas as suas fontes disponíveis (não possui OpenSSL, etc ...) e seu único fallback é mt_rand (), ainda é melhor que a alternativa (que é rand ()).
Andrew Moore

4
@AndrewMoore: absolutamente. Sem discutir isso. Só que mt_rande uniqid(e, portanto, lcg_valuee rand) não são primeiras escolhas ...
ircmaxell

1
ircmaxell, muito obrigado pela biblioteca password_compat para 5.3.xx, não precisávamos disso antes, mas agora precisamos, em um servidor php 5.3.xx, e obrigado por seu conselho claro de não tentar fazer essa lógica a si mesmo.
Lizardx

47

Você encontrará muitas informações em Suficiente com as tabelas Rainbow: o que você precisa saber sobre esquemas de senha segura ou estrutura portátil de hash de senha PHP .

O objetivo é hash a senha com algo lento, para que alguém que obtenha seu banco de dados de senha morra tentando forçá-la (um atraso de 10 ms para verificar uma senha não é nada para você, muito para alguém que tenta forçá-la). O Bcrypt é lento e pode ser usado com um parâmetro para escolher o quão lento é.


7
Aplique o que você quiser, os usuários conseguirão errar e usar a mesma senha em várias coisas. Portanto, você deve protegê-lo o máximo possível ou implementar algo que não permita que você armazene nenhuma senha (SSO, openID etc.).
Arkh

41
Não. O hash de senha é usado para proteger contra um ataque: alguém roubou seu banco de dados e deseja obter logon em texto não criptografado + senhas.
Arkh

4
@ Josh K. Eu encorajo você a tentar decifrar algumas senhas simples depois de passar pelo sintonizador phpass, de modo que leva entre 1 ms e 10 ms para computá-las em seu servidor da web.
Arkh

3
Acordado. Mas o tipo de usuário que usará qwerty como senha também é o tipo de usuário que marcará qualquer um complicado em algum lugar em que ele (e os atacantes) possam lê-lo facilmente. O que o uso do bcrypt realiza é que, quando o seu banco de dados for público contra a sua vontade, será mais difícil acessar os usuários que possuem uma senha como ^ | $$ & ZL6- £ do que se você usasse o sha512 de uma só vez.
Arkh

4
@coreyward vale a pena notar que fazer isso é mais prejudicial do que não bloquear; isso é facilmente considerado um vetor de "negação de serviço". Basta começar a enviar logins incorretos com spam em qualquer conta conhecida e você pode atrapalhar muitos usuários com muita facilidade. É melhor encerrar (atrasar) o invasor do que negar completamente o acesso, especialmente se for um cliente pagador.
Damianb

36

Você pode criar um hash unidirecional com o bcrypt usando a crypt()função PHP e passando um sal Blowfish apropriado. A mais importante de toda a equação é que A) o algoritmo não foi comprometido e B) você salta corretamente cada senha . Não use sal de aplicação geral; que abre todo o aplicativo para atacar a partir de um único conjunto de tabelas Rainbow.

PHP - Função Crypt


4
Esta é a abordagem correta - use a crypt()função PHP , que suporta várias funções diferentes de hash de senha. Verifique se você não está usando CRYPT_STD_DESou CRYPT_EXT_DES- qualquer um dos outros tipos suportados está correto (e inclui bcrypt, sob o nome CRYPT_BLOWFISH).
caf 27/01

4
O SHA também possui um parâmetro de custo, através da opção 'rounds'. Ao usar isso, também não vejo razão para favorecer o bcrypt.
Pieter Ennes

3
Na verdade, um único SHA-1 (ou MD5) de uma senha ainda é facilmente capaz de força bruta, com ou sem sal (o sal ajuda nas tabelas do arco-íris, não na força bruta). Use bcrypt.
Paŭlo Ebermann 9/09/11

Acho perturbador que todo mundo pareça dizer "bcrypt" quando se refere ao crypt do php ().
Sliq

3
@Panique Por quê? O algoritmo é chamado bcrypt . cryptexpõe vários hashes de senha, com bcrypt correspondente à CRYPT_BLOWFISHconstante. Atualmente, o Bcrypt é o algoritmo mais forte suportado por cryptvários outros que ele suporta são bastante fracos.
#

34

Edit: 15.01.2013 - Se o seu servidor oferecer suporte, use a solução de martinstoeckli .


Todo mundo quer tornar isso mais complicado do que é. A função crypt () faz a maior parte do trabalho.

function blowfishCrypt($password,$cost)
{
    $chars='./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    $salt=sprintf('$2y$%02d$',$cost);
//For PHP < PHP 5.3.7 use this instead
//    $salt=sprintf('$2a$%02d$',$cost);
    //Create a 22 character salt -edit- 2013.01.15 - replaced rand with mt_rand
    mt_srand();
    for($i=0;$i<22;$i++) $salt.=$chars[mt_rand(0,63)];
    return crypt($password,$salt);
}

Exemplo:

$hash=blowfishCrypt('password',10); //This creates the hash
$hash=blowfishCrypt('password',12); //This creates a more secure hash
if(crypt('password',$hash)==$hash){ /*ok*/ } //This checks a password

Sei que deve ser óbvio, mas não use 'senha' como sua senha.


3
A criação do sal poderia ser melhorada (use a fonte aleatória do sistema operacional), caso contrário, isso me parece bom. Para versões mais recentes do PHP, é melhor usar em 2yvez de 2a.
martinstoeckli

use mcrypt_create_iv($size, MCRYPT_DEV_URANDOM)como fonte para o sal.
CodesInChaos

Vou dar uma olhada em mcrypt_create_iv () quando chegar um momento, se nada mais deve melhorar um pouco o desempenho.
precisa saber é o seguinte

2
Adicione a codificação Base64 e traduza para os bcryptusos personalizados do alfabeto . mcrypt_create_iv(17, MCRYPT_DEV_URANDOM), str_replace('+', '.', base64_encode($rawSalt)),$salt = substr($salt, 0, 22);
CodesInChaos

1
@ JonHulka - Veja o pacote de compatibilidade do PHP [Linha 127], esta é uma implementação direta.
martinstoeckli

29

A versão 5.5 do PHP terá suporte embutido para BCrypt, as funções password_hash()e password_verify(). Na verdade, são apenas invólucros em torno da função crypt()e facilitarão o uso correto. Ele cuida da geração de um sal aleatório seguro e fornece bons valores padrão.

A maneira mais fácil de usar essas funções será:

$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);
$isPasswordCorrect = password_verify($password, $existingHashFromDb);

Esse código hash a senha com BCrypt (algoritmo 2y), gera um sal aleatório a partir da fonte aleatória do SO e usa o parâmetro de custo padrão (no momento é 10). A segunda linha verifica se o usuário digitou a senha corresponde a um valor de hash já armazenado.

Se você deseja alterar o parâmetro cost, pode fazê-lo assim, aumentando o parâmetro cost em 1, dobra o tempo necessário para calcular o valor do hash:

$hash = password_hash($password, PASSWORD_BCRYPT, array("cost" => 11));

Ao contrário do "cost"parâmetro, é melhor omitir o "salt"parâmetro, porque a função já faz o possível para criar um sal criptograficamente seguro.

Para o PHP versão 5.3.7 e posterior, existe um pacote de compatibilidade , do mesmo autor que fez a password_hash()função. Para versões do PHP anteriores 5.3.7 não há suporte para crypt()com 2yo algoritmo bcrypt segura unicode. Pode-se substituí-lo por 2a, que é a melhor alternativa para versões anteriores do PHP.


3
Depois de ler isso, meu primeiro pensamento foi "como você armazena o sal que é gerado"? Após examinar os documentos, a função password_hash () acaba gerando uma sequência que armazena o método de criptografia, o salt e o hash gerado. Portanto, ele armazena tudo o que precisa em uma string para que a função password_verify () funcione. Só queria mencionar isso, pois pode ajudar os outros quando vêem isso.
usar o seguinte código

@ jzimmerman2011 - Exatamente, em outra resposta, tentei explicar esse formato de armazenamento com um exemplo.
martinstoeckli

7

Pensamento atual: os hashes devem ser os mais lentos disponíveis, não os mais rápidos possíveis. Isso suprime os ataques da tabela arco-íris .

Também relacionado, mas por precaução: um invasor nunca deve ter acesso ilimitado à sua tela de login. Para evitar isso: Configure uma tabela de rastreamento de endereço IP que registre cada ocorrência junto com o URI. Se mais de 5 tentativas de login vierem do mesmo endereço IP em um período de cinco minutos, bloqueie com explicação. Uma abordagem secundária é ter um esquema de senha de duas camadas, como os bancos. A colocação de um bloqueio para falhas na segunda passagem aumenta a segurança.

Resumo: diminua a velocidade do invasor usando funções hash demoradas. Além disso, bloqueie muitos acessos ao seu login e adicione uma segunda camada de senha.


Acho que eles assumem que o invasor já conseguiu roubar meu banco de dados por outros meios e agora está tentando obter as senhas para poder usá-las no paypal ou algo assim.
Vilx-

4
No meio de 2012 e essa resposta ainda é instável, como um algoritmo de hash lento evita ataques à tabela do arco-íris? Eu pensei que um sal aleatório de intervalo de bytes fez? Eu sempre pensei que a velocidade do algoritmo de hash dita quantas iterações eles podem enviar contra o hash obtido de você em um período específico de tempo. Também NUNCA BLOCO UM USUÁRIO EM TENTATIVAS DE LOGIN FALHADAS confie em mim, seus usuários ficarão cansados, geralmente em alguns sites eu preciso fazer login quase 5 vezes, às vezes mais, antes de me lembrar da minha senha. Além disso, o segundo nível de passe não funciona, embora a autenticação em duas etapas com o código do celular possa funcionar.
Sammaye

1
@ Sammaye, eu concordo com isso até certo ponto. Eu configurei um bloco em 5 tentativas de login com falha, antes de aumentá-lo rapidamente para 7, depois 10 agora está em 20. Nenhum usuário normal deve ter 20 tentativas de login com falha, mas é baixo o suficiente para interromper facilmente ataques de força bruta
Bruce Aldridge,

@BruceAldridge Eu pessoalmente acho que seria melhor interromper o script por um tempo aleatório depois de, digamos, 7 logins com falha e mostrar um captcha em vez de bloquear. O bloqueio é uma ação muito agressiva a ser tomada.
Sammaye

1
@ Sammaye Concordo que bloqueios permanentes são ruins. Estou me referindo a um bloco temporário que aumenta com o número de tentativas com falha.
Bruce Aldridge

7

Aqui está uma resposta atualizada para essa pergunta antiga!

A maneira correta de hash senhas no PHP desde 5.5 é com password_hash(), e a maneira correta de verificá-las é com password_verify(), e isso ainda é verdade no PHP 8.0. Essas funções usam hashes bcrypt por padrão, mas outros algoritmos mais fortes foram adicionados. Você pode alterar o fator de trabalho (efetivamente quão forte é a criptografia) através dos password_hashparâmetros.

No entanto, embora ainda seja suficientemente forte, o bcrypt não é mais considerado o estado da arte ; chegou um conjunto melhor de algoritmos de hash de senha chamado Argon2 , com as variantes Argon2i, Argon2d e Argon2id. A diferença entre eles (como descrito aqui ):

O Argon2 tem uma variante principal: Argon2id e duas variantes suplementares: Argon2d e Argon2i. O Argon2d usa acesso à memória dependente de dados, o que o torna adequado para aplicações de criptomoedas e prova de trabalho sem ameaças de ataques de sincronização de canal lateral. O Argon2i usa acesso à memória independente de dados, o que é preferido para hash de senha e derivação de chave baseada em senha. O Argon2id funciona como Argon2i na primeira metade da primeira iteração sobre a memória e como Argon2d no restante, fornecendo proteção contra ataques de canal lateral e economia de custos de força bruta devido a trocas de memória de tempo.

O suporte ao Argon2i foi adicionado no PHP 7.2, e você solicita assim:

$hash = password_hash('mypassword', PASSWORD_ARGON2I);

e suporte ao Argon2id foi adicionado no PHP 7.3:

$hash = password_hash('mypassword', PASSWORD_ARGON2ID);

Nenhuma alteração é necessária para verificar as senhas, pois a sequência de hash resultante contém informações sobre quais algoritmos, sal e fatores de trabalho foram usados ​​quando foram criados.

De maneira bem separada (e um tanto redundante), o libsodium (adicionado no PHP 7.2) também fornece hash do Argon2 através das funções sodium_crypto_pwhash_str ()e sodium_crypto_pwhash_str_verify(), que funcionam da mesma maneira que os embutidos do PHP. Uma razão possível para usá-las é que o PHP às vezes pode ser compilado sem o libargon2, o que torna os algoritmos do Argon2 indisponíveis para a função password_hash; O PHP 7.2 e superior sempre devem ter o libsodium ativado, mas pode não - mas pelo menos existem duas maneiras de obter esse algoritmo. Aqui está como você pode criar um hash Argon2id com libsodium (mesmo no PHP 7.2, que não possui suporte ao Argon2id):

$hash = sodium_crypto_pwhash_str(
    'mypassword',
    SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
    SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);

Observe que ele não permite que você especifique um sal manualmente; isso faz parte do ethos da libsodium - não permita que os usuários definam parâmetros para valores que possam comprometer a segurança - por exemplo, nada impede que você passe uma string de sal vazia para a password_hashfunção do PHP ; libsodium não permite que você faça nada tão bobo!



1

Como todos sabemos, armazenar a senha em texto não criptografado no banco de dados não é seguro. o bcrypt é uma técnica de senha com hash. É usada para criar segurança de senha. uma das funções surpreendentes do bcrypt é nos salvar dos hackers; é usado para proteger a senha contra ataques de hackers, porque a senha é armazenada na forma bcrypted.

a função password_hash () é usada para criar um novo hash de senha. Ele usa um algoritmo de hash forte e robusto. A função password_hash () é muito compatível com a função crypt (). Portanto, os hashes de senha criados por crypt () podem ser usados ​​com password_hash () e vice-versa. As funções password_verify () e password_hash () são apenas os wrappers da função crypt (), e facilitam muito o uso preciso.

SINTAXE

string password_hash($password , $algo , $options)

Atualmente, os seguintes algoritmos são suportados pela função password_hash ():

PASSWORD_DEFAULT PASSWORD_BCRYPT PASSWORD_ARGON2I PASSWORD_ARGON2ID

Parâmetros: Esta função aceita três parâmetros, como mencionado acima e descrito abaixo:

senha : armazena a senha do usuário. algo : É a constante do algoritmo de senha que é usada continuamente enquanto denota o algoritmo que deve ser usado quando o hash da senha ocorrer. options : É uma matriz associativa, que contém as opções. Se isso for removido e não incluir, um sal aleatório será usado e a utilização de um custo padrão ocorrerá. Valor de retorno : retorna a senha com hash em caso de sucesso ou False em caso de falha.

Exemplo :

Input : echo password_hash("GFG@123", PASSWORD_DEFAULT); Output : $2y$10$.vGA19Jh8YrwSJFDodbfoHJIOFH)DfhuofGv3Fykk1a

Os programas abaixo ilustram a função password_hash () no PHP:

<?php echo password_hash("GFG@123", PASSWORD_DEFAULT); ?>

RESULTADO

$2y$10$Z166W1fBdsLcXPVQVfPw/uRq1ueWMA6sLt9bmdUFz9AmOGLdM393G

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.