Senhas na mira: por que métodos antigos não funcionam mais
\nImagine: você é um desenvolvedor criando um sistema de registro. Parece simples, não? Pegue a senha, passe pelo md5, salve no banco. Mas em 2025, essa abordagem é caminho direto para a vulnerabilidade. Ataques modernos, GPU-bruteforce e tabelas rainbow transformam hashes fracos em pó em segundos. A pergunta do Stack Overflow com milhares de visualizações — "Secure hash and salt for PHP passwords" — não perde relevância, e vamos resolvê-la de uma vez por todas.
\nNeste artigo, você aprenderá como fazer hash de senhas corretamente em PHP, usando algoritmos modernos, funções nativas e salt. Adaptaremos as melhores respostas da comunidade para conteúdo educacional e mostraremos como proteger usuários mesmo em 2025.
\n\nPor que md5 e sha1 são o mal
\nSe você ainda usa md5 ou sha1 para armazenar senhas — pare. Esses algoritmos foram criados para velocidade, não para segurança. Em 2025, uma GPU pode gerar bilhões de hashes md5 por segundo. Adicionar salt (string aleatória) não salva: tabelas rainbow são contornadas, mas a velocidade continua fatal.
\n- \n
- Md5: 2^128 variações — quebrado em minutos. \n
- Sha1: um pouco melhor, mas também obsoleto. \n
- Salt sem lentidão: se o hash é rápido, o salt apenas adia o ataque. \n
Em vez disso, precisamos de algoritmos com "lentidão" — que exigem tempo e memória. É exatamente isso que o password_hash usa.
password_hash e password_verify: solução nativa do PHP
\nDesde o PHP 5.5, existem duas funções que resolvem 90% dos problemas de segurança de senhas: password_hash e password_verify. Elas geram automaticamente um salt criptograficamente seguro e usam algoritmos resistentes a bruteforce. Em 2025, este é o padrão, nada de soluções caseiras!
Como funciona?
\nA função password_hash recebe a senha e o algoritmo (padrão é bcrypt, mas pode usar argon2). Ela retorna uma string que já contém o salt e os parâmetros. Você não precisa armazenar o salt separadamente — ele está dentro do hash.
<?php\n// Fazendo hash da senha\n$password = "SuperSecret123!";\n$hash = password_hash($password, PASSWORD_DEFAULT);\n\necho $hash; // $2y$10$... (60 caracteres)\n?>\n\nAgora, verificamos a senha no login:
\n<?php\n$inputPassword = "SuperSecret123!";\n$storedHash = "$2y$10$..."; // do banco de dados\n\nif (password_verify($inputPassword, $storedHash)) {\n echo "Senha correta!";\n} else {\n echo "Senha incorreta.";\n}\n?>\n\nSimples e genial. O password_verify extrai automaticamente o salt do hash e verifica a senha. Nenhuma ação extra necessária.
Escolha do algoritmo: bcrypt vs argon2
\nEm 2025, você tem duas opções principais:
\n- \n
- Bcrypt (PASSWORD_BCRYPT): testado pelo tempo, usado por padrão. Resistente a ataques, mas requer configuração do cost factor. \n
- Argon2 (PASSWORD_ARGON2I ou PASSWORD_ARGON2ID): vencedor do Password Hashing Competition. Mais moderno, exige mais memória, dificultando ataques com GPU. Recomendado para novos projetos. \n
Exemplo com argon2:
\n<?php\n$hash = password_hash($password, PASSWORD_ARGON2ID, [\n 'memory_cost' => 1 << 17, // 128 MB\n 'time_cost' => 4,\n 'threads' => 2\n]);\n?>\n\nO parâmetro memory_cost é a quantidade de memória em kilobytes. Quanto maior, mais seguro, porém mais lento. Para produção, teste no seu servidor.
Salt: como funciona e por que não é necessário separadamente
\nMuitos iniciantes perguntam: "Como adicionar salt manualmente?". Resposta: de jeito nenhum. O password_hash gera 22 caracteres de salt aleatório (para bcrypt