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_hashno cadastro - como usar
password_verifyno 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:
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:
- senha
No backend (PHP), você faria algo assim:
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:
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:
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:
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
md5ousha1para senhas - Guardar senha em texto puro
- Comparar a senha digitada com o hash usando
==ou=== - Tentar usar
hash_equalsou outras funções para “reinventar” o processo
Para senhas, o caminho certo é sempre:
password_hashpara gerar o hashpassword_verifypara 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,sha1ou hash manual para senhas - sempre use
password_hashno cadastro - sempre use
password_verifyno login - use
password_needs_rehashse 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.