Por que a autenticação "feita em casa" é uma bomba-relógio
\n\nTodo segundo desenvolvedor PHP já escreveu pelo menos uma vez seu próprio sistema de login. Parece simples: formulário, verificação de login, sessão. Mas é exatamente aqui, nesta etapa "simples", que milhões de sites morrem. Injeções SQL, roubo de sessões, força bruta de senhas — ataques que transformam seu projeto em um vazamento de dados pessoais.
\n\nNeste artigo, vamos abordar a autenticação segura em PHP do zero. Sem mágica — apenas práticas comprovadas usadas no Facebook, GitHub e no seu framework favorito. Você aprenderá como fazer hash de senhas corretamente, proteger sessões contra interceptação e implementar mecanismos modernos como JWT.
\n\nPronto? Vamos lá.
\n\n1. Hash de senhas: por que md5() é uma sentença de morte
\n\nA primeira regra de segurança: nunca, jamais armazene senhas em texto puro. A segunda regra: não use algoritmos obsoletos como MD5 ou SHA1. Eles são calculados em microssegundos, e qualquer estudante com um notebook quebrará sua senha em uma hora.
\n\nO PHP tem uma solução nativa — as funções password_hash() e password_verify(). Elas usam bcrypt (ou Argon2, se PHP ≥ 7.2) e adicionam automaticamente um "sal" — uma string aleatória que torna cada hash único.
Exemplo de hash correto
\n// Registro de usuário\n$password = $_POST['password'];\n$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);\n\n// Salve $hash no banco de dados\n// Nenhum sal manual! O PHP faz tudo sozinho.\n\n\nVerificação de senha no login
\n// Login do usuário\n$inputPassword = $_POST['password'];\n$storedHash = 'do banco de dados';\n\nif (password_verify($inputPassword, $storedHash)) {\n // Senha correta — permitir acesso\n} else {\n // Senha incorreta\n}\n\n\nObserve o parâmetro cost. Ele define o "custo" do cálculo do hash. Quanto maior o número, mais tempo o invasor levará para quebrar a senha. Para 2024, cost = 12 é o equilíbrio ideal entre segurança e velocidade.
2. Sessões: como não entregar as chaves de casa
\n\nApós o login bem-sucedido, geralmente iniciamos uma sessão. Mas as sessões padrão do PHP têm pontos fracos: IDs previsíveis, vazamento via URL, fixação de sessão (session fixation).
\n\nConfiguração segura de sessão
\n// No início do script, antes de qualquer saída HTML\nini_set('session.use_only_cookies', 1);\nini_set('session.use_strict_mode', 1);\nini_set('session.cookie_httponly', 1);\nini_set('session.cookie_secure', 1); // Apenas para HTTPS\nini_set('session.cookie_samesite', 'Lax');\n\nsession_start();\n\n// Regenerar o ID da sessão após o login\nif ($login_success) {\n session_regenerate_id(true);\n $_SESSION['user_id'] = $user['id'];\n}\n\n\nO que fizemos:
\n- \n
- use_only_cookies — proibimos a transmissão do ID da sessão via URL (para evitar roubo através de links) \n
- use_strict_mode — bloqueamos sessões não inicializadas \n
- httponly — impedimos o acesso JavaScript ao cookie da sessão (proteção contra XSS) \n
- secure — o cookie é transmitido apenas via HTTPS \n
- samesite — proteção contra ataques CSRF através de falsificação de requisições entre sites \n
E o principal: sempre regenere o ID da sessão após o login. Caso contrário, um invasor pode fornecer antecipadamente seu próprio session ID ao usuário e sequestrar a conta.
\n\n3. Proteção contra CSRF: verificação de que a requisição foi enviada por você
\n\nCSRF (Cross-Site Request Forgery) — um ataque onde o usuário clica em um link como /delete-account?id=1 e, sem querer, executa uma ação. Para evitar isso, use tokens.
Geração e verificação do token CSRF
\n// Geração do token (no início da sessão ou ao carregar o formulário)\nif (empty(