No outro artigo, falamos sobre o hash_equals e como ele ajuda a comparar valores sensíveis com mais segurança. Lá, fizemos uma observação importante: para senhas, não usamos hash_equals, e sim password_hash + password_verify.

Esse assunto rende facilmente um post inteiro, porque trabalhar com senhas é uma das partes mais críticas de qualquer sistema.
Se você erra aqui, coloca em risco todos os seus usuários.

Neste artigo, vamos ver:

  • por que nunca devemos guardar senhas em texto puro
  • o que é um hash de senha
  • como usar password_hash no cadastro
  • como usar password_verify no login
  • como ajustar configurações sem complicar demais
  • como atualizar hashes antigos

A ideia é que você termine esse texto realmente entendendo o que está fazendo, não só copiando código.

Por que nunca guardar senhas em texto puro

Vamos começar pelo básico:
Guardar senha em texto puro (do jeito que o usuário digita) é um dos maiores erros que podem existir em um sistema.

O que acontece se:

  • o banco de dados vaza
  • alguém interno tem acesso indevido
  • um backup fica exposto em algum lugar

Se as senhas estiverem salvas como texto, qualquer pessoa que conseguir esse arquivo vai ver:

  • e-mails
  • senhas
  • e ainda pode testar esses dados em outros sites (muita gente reutiliza a mesma senha em tudo)

Por isso, nunca devemos gravar a senha original.
Em vez disso, guardamos apenas um hash dessa senha.

O que é um hash de senha (sem complicar)

Você pode pensar no hash como uma “impressão digital” da senha.

Você digita uma senha: minhaSenhaSecreta123

O PHP transforma isso em uma sequência longa e estranha, algo como:
$2y$10$uV9hZtJNf3uM...

Essa sequência:

  • não permite descobrir qual era a senha original
  • sempre muda se a senha mudar
  • pode ser comparada na hora do login para saber se a senha digitada é a mesma de antes

O ponto mais importante é:
o banco guarda só o hash, não a senha.

E é aqui que entra o password_hash.

O que é password_hash e por que ele é tão importante

password_hash é uma função do PHP criada especificamente para gerar hashes de senha da forma certa, sem você precisar entender todos os detalhes criptográficos por trás.

Ele cuida automaticamente de coisas que são fáceis de errar quando fazemos “na mão”, como:

  • escolher um algoritmo adequado
  • gerar um salt seguro
  • definir um custo adequado
  • guardar tudo isso em um único hash

Você passa a senha original e ele devolve um hash pronto para ser armazenado no banco.

Exemplo simples:

$senhaDigitada = 'minhaSenhaSecreta123'; 
$hash = password_hash($senhaDigitada, PASSWORD_DEFAULT); // aqui você grava $hash no banco de dados 

Repare que você nunca salva a senha, apenas o hash.

Como usar password_hash no cadastro de usuário

Vamos simular um cenário de cadastro básico.

No formulário, o usuário preenche:

  • e-mail
  • senha

No backend (PHP), você faria algo assim:

$email = $_POST['email'] ?? ''; 
$senha = $_POST['senha'] ?? ''; 
// Faça validações aqui: e-mail válido, senha com tamanho mínimo, etc. 
$hash = password_hash($senha, PASSWORD_DEFAULT); 
// Depois, grava no banco algo como: 
// id | email           | senha_hash 
// 1  | teste@teste.com | $hash 
// Exemplo (simples, sem PDO ou CI só para ilustrar a lógica): 
// INSERT INTO usuarios (email, senha_hash) VALUES ($email, $hash); 

A regra é clara:
na tabela de usuários você guarda senha_hash, nunca “senha”.

Como usar password_verify no login

Na hora do login, o fluxo é um pouco diferente:

Você recebe o e-mail e a senha digitada pelo usuário

Busca no banco o registro desse usuário pelo e-mail

Recupera o hash que está salvo

Usa password_verify para saber se a senha digitada é a mesma que gerou aquele hash

Exemplo:

$email = $_POST['email'] ?? ''; 
$senhaDigitada = $_POST['senha'] ?? ''; // 1. Buscar o usuário no banco 
// SELECT senha_hash FROM usuarios WHERE email = $email; // vamos supor que veio isso do banco: $hashNoBanco = '$2y$10$uV9hZtJNf3uM...'; 
// valor de exemplo 
// 2. Verificar a senha 
if (password_verify($senhaDigitada, $hashNoBanco)) {    
	// Senha correta    
	echo "Login permitido";    
	// Aqui você inicia a sessão, salva dados na session, etc. 
} else {    
	// Senha incorreta    
	echo "E-mail ou senha inválidos"; 
}

Repare que em nenhum momento você “desfaz” o hash.
Você sempre compara a senha digitada com o hash usando password_verify.

Ajustando opções avançadas sem complicar (cost)

Por padrão, o password_hash já escolhe um algoritmo adequado (definido por PASSWORD_DEFAULT) e um custo razoável.

Mas você pode ajustar o custo para deixar o processo mais “pesado” (mais seguro, porém mais lento) se quiser:

$options = [   
	 'cost' => 12, // padrão normalmente é 10 
]; 
$hash = password_hash($senhaDigitada, PASSWORD_DEFAULT, $options);

Regra prática:

Quanto maior o cost, mais tempo o servidor leva para gerar/verificar o hash.

Escolha um valor que seja seguro, mas que não deixe o login lento demais. 

Para a maioria dos sistemas pequenos e médios, o padrão já é suficiente

Atualizando hashes antigos com password_needs_rehash

Vamos supor que:

Você começou usando um custo menor ou no futuro o PASSWORD_DEFAULT for atualizado para um algoritmo melhor.

Você pode verificar, na hora do login, se o hash antigo precisa ser atualizado.

Exemplo:

$senhaDigitada = $_POST['senha'] ?? '';
$hashNoBanco   = $usuario['senha_hash']; // vindo do banco

if (password_verify($senhaDigitada, $hashNoBanco)) {
    // Login permitido

    // Verifica se o hash precisa ser atualizado
    $options = ['cost' => 12];

    if (password_needs_rehash($hashNoBanco, PASSWORD_DEFAULT, $options)) {
        $novoHash = password_hash($senhaDigitada, PASSWORD_DEFAULT, $options);

        // Atualiza o hash no banco
        // UPDATE usuarios SET senha_hash = $novoHash WHERE id = ...
    }

    echo "Login bem-sucedido";
} else {
    echo "E-mail ou senha inválidos";
}

Esse é um cuidado extra que deixa seu sistema preparado para o futuro.

Erros comuns que você deve evitar

Alguns erros que são bem comuns (e perigosos):

  • Usar md5 ou sha1 para senhas
  • Guardar senha em texto puro
  • Comparar a senha digitada com o hash usando == ou ===
  • Tentar usar hash_equals ou outras funções para “reinventar” o processo

Para senhas, o caminho certo é sempre:

  • password_hash para gerar o hash
  • password_verify para validar a senha

Isso já resolve a maior parte dos problemas de segurança relacionados a armazenamento de senhas.

Como esse tema se conecta com o post do hash_equals

Se você leu o artigo sobre hash_equals, deve lembrar que ele é excelente para comparar:

  • tokens
  • chaves
  • códigos sensíveis em geral

Mas também comentamos que ele não é o jeito certo de lidar com senhas.

Por quê?

Porque senhas não são apenas “strings importantes”
Elas precisam de um tratamento completo:

  • hashing adequado
  • salt automático
  • resistência a ataques de força bruta
  • possibilidade de migração de algoritmo

E tudo isso já está embutido no combo password_hash + password_verify.

Conclusão: password_hash e password_verify devem ser padrão em qualquer sistema com login

Se você está construindo qualquer aplicação que tenha:

  • cadastro de usuário
  • login
  • área restrita

então password_hash e password_verify não são opcionais:
eles são o padrão mínimo para trabalhar com senhas de forma profissional.

Resumindo:

  • nunca guarde senhas em texto puro
  • nunca use md5, sha1 ou hash manual para senhas
  • sempre use password_hash no cadastro
  • sempre use password_verify no login
  • use password_needs_rehash se quiser manter o sistema preparado para o futuro

Com isso bem implementado, você já está vários passos à frente da maioria dos sistemas que ainda tratam senhas de forma descuidada.