Autenticação
Nesta aula, você aprenderá a implementar autenticação segura em PHP, cobrindo desde o login com sessões até o logout, incluindo a funcionalidade 'lembrar-me' e boas práticas de segurança. O conteúdo inclui exemplos práticos, exercícios com respostas e referências oficiais para aprofundamento.
Autenticação é o processo de verificar a identidade de um usuário, geralmente através de credenciais como e-mail e senha. Em aplicações web, após a autenticação, o sistema precisa manter o estado do usuário logado durante a navegação. Nesta aula, vamos explorar como implementar um sistema de login robusto em PHP, utilizando sessões, cookies, e técnicas de segurança para proteger contra ataques comuns.
Vamos começar com os conceitos básicos de sessão e login, depois avançaremos para recursos como 'lembrar-me', que mantém o usuário autenticado por mais tempo, e finalizaremos com boas práticas essenciais para produção.
Login e sessão
O login é o ponto de entrada para áreas restritas de uma aplicação. Em PHP, a forma mais comum de manter o usuário autenticado é através de sessões. Uma sessão é um mecanismo que armazena dados do usuário no servidor, associados a um identificador único (ID de sessão) que é enviado ao cliente via cookie. Quando o usuário faz login, validamos as credenciais e, se forem corretas, armazenamos dados como o ID do usuário na variável superglobal $_SESSION.
Para iniciar uma sessão, usamos session_start() no início do script. É importante que essa função seja chamada antes de qualquer saída HTML. Vamos ver um exemplo de script de login:
<?php
session_start();
// Simulando um banco de dados de usuários (em produção, use um banco real)
$usuarios = [
['id' => 1, 'email' => 'user@example.com', 'senha_hash' => password_hash('senha123', PASSWORD_DEFAULT)],
];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = $_POST['email'] ?? '';
$senha = $_POST['senha'] ?? '';
// Buscar usuário pelo e-mail (em produção, use prepared statements)
$usuario = null;
foreach ($usuarios as $u) {
if ($u['email'] === $email) {
$usuario = $u;
break;
}
}
if ($usuario && password_verify($senha, $usuario['senha_hash'])) {
// Regenerar ID da sessão para evitar fixação
session_regenerate_id(true);
$_SESSION['user_id'] = $usuario['id'];
$_SESSION['logged_in'] = true;
header('Location: dashboard.php');
exit;
} else {
$erro = 'Credenciais inválidas.';
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<?php if (isset($erro)): ?>
<p style="color: red;"><?= htmlspecialchars($erro) ?></p>
<?php endif; ?>
<form method="post">
<label>E-mail: <input type="email" name="email" required></label><br>
<label>Senha: <input type="password" name="senha" required></label><br>
<button type="submit">Entrar</button>
</form>
</body>
</html>
No exemplo acima, após verificar as credenciais, chamamos session_regenerate_id(true) para evitar ataques de fixação de sessão. Em seguida, armazenamos o ID do usuário na sessão. Em páginas protegidas, verificamos se $_SESSION['logged_in'] está definido e se o usuário existe.
Lembrar-me
A funcionalidade "lembrar-me" permite que o usuário permaneça autenticado por um período prolongado, mesmo após fechar o navegador. Isso é implementado com cookies de longa duração, mas armazenar o ID do usuário em um cookie é inseguro. A prática recomendada é usar tokens aleatórios armazenados em uma tabela no banco de dados, associados ao usuário e com data de expiração.
Vamos criar um fluxo básico: quando o usuário marca "lembrar-me", geramos um token aleatório, armazenamos um hash dele no banco, e enviamos o token em um cookie. Quando o usuário retorna sem sessão ativa, verificamos o cookie e autenticamos via token.
// No login, se 'lembrar' estiver marcado:
if (!empty($_POST['lembrar'])) {
$token = bin2hex(random_bytes(32));
$token_hash = hash('sha256', $token);
// Salvar $token_hash e expiração no banco para o usuário
// Exemplo: INSERT INTO user_tokens (user_id, token_hash, expires_at) VALUES (?, ?, ?)
setcookie('remember_token', $token, time() + 30 * 24 * 3600, '/', '', true, true); // 30 dias, HttpOnly, Secure
}
// No início de cada requisição, se não houver sessão:
if (!isset($_SESSION['user_id']) && isset($_COOKIE['remember_token'])) {
$token = $_COOKIE['remember_token'];
$token_hash = hash('sha256', $token);
// Buscar token no banco e verificar expiração
// Se válido, regenerar sessão e logar usuário
}
É importante definir o cookie com as flags HttpOnly (para não ser acessível via JavaScript) e Secure (para ser enviado apenas via HTTPS). Além disso, o token deve ser invalidado após o uso (por exemplo, rotacionar o token a cada login).
Logout seguro
O logout deve encerrar a sessão e limpar os cookies de forma segura. Além de destruir a sessão, devemos invalidar os tokens de "lembrar-me" no banco e limpar o cookie.
// logout.php
session_start();
// Limpar cookie de lembrar-me
if (isset($_COOKIE['remember_token'])) {
// Excluir token do banco
$token_hash = hash('sha256', $_COOKIE['remember_token']);
// DELETE FROM user_tokens WHERE token_hash = ?
setcookie('remember_token', '', time() - 3600, '/', '', true, true);
}
// Destruir a sessão
$_SESSION = [];
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
session_destroy();
header('Location: login.php');
exit;
Esse código limpa todos os dados da sessão, remove o cookie de sessão e o cookie de lembrar-me, e redireciona para a página de login. Sempre use header('Location: ...') após o logout para evitar que o usuário veja conteúdo protegido.
Boas práticas
Implementar autenticação exige atenção a várias práticas de segurança. Aqui estão as principais:
- Senhas: Nunca armazene senhas em texto puro; use
password_hash()epassword_verify(). - HTTPS: Use HTTPS em todo o site para proteger as credenciais e cookies.
- Proteção contra CSRF: Em formulários de login e logout, use tokens CSRF para evitar requisições forjadas.
- Limitar tentativas de login: Implemente bloqueio após várias tentativas falhas para evitar força bruta.
- Validação de entrada: Sempre valide e filtre dados de entrada.
- Regenerar ID de sessão: Ao fazer login, chame
session_regenerate_id(true). - Expiração de sessão: Defina um tempo de expiração para a sessão e encerre após inatividade.
- Logs de auditoria: Registre eventos de login e logout para monitoramento.
Além disso, considere usar bibliotecas ou frameworks como Laravel ou Symfony, que já possuem sistemas de autenticação robustos e testados.
Referências
- PHP Manual - Sessions
- PHP Manual - password_hash
- OWASP - Session Fixation
- OWASP - CSRF
- MDN - Cookies HTTP
- PHP Manual - Cookies
- OWASP Top 10
Exercícios
- Implemente um script de login que verifica as credenciais usando
password_verify()e inicia a sessão. Use um array de usuários em memória. - Adicione a funcionalidade "lembrar-me" ao script de login, gerando um token e armazenando no banco (simule com um array).
- Crie um script de logout que limpe a sessão e o cookie de lembrar-me, e destrua a sessão.
- Implemente uma função para verificar se o usuário está logado em uma página protegida, redirecionando para login se não estiver.
- Adicione proteção CSRF ao formulário de login, gerando um token e validando-o no POST.
<?php
session_start();
$usuarios = [
['id' => 1, 'email' => 'user@example.com', 'senha_hash' => password_hash('senha123', PASSWORD_DEFAULT)],
];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = $_POST['email'];
$senha = $_POST['senha'];
foreach ($usuarios as $u) {
if ($u['email'] === $email && password_verify($senha, $u['senha_hash'])) {
session_regenerate_id(true);
$_SESSION['user_id'] = $u['id'];
header('Location: dashboard.php');
exit;
}
}
echo 'Credenciais inválidas.';
}
?>
<?php
session_start();
$usuarios = []; // simulação
$tokens = []; // armazena hash => user_id e expiração
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// ... valida credenciais ...
if ($usuario_valido) {
session_regenerate_id(true);
$_SESSION['user_id'] = $id;
if (isset($_POST['lembrar'])) {
$token = bin2hex(random_bytes(32));
$hash = hash('sha256', $token);
$tokens[$hash] = ['user_id' => $id, 'expires' => time() + 30*24*3600];
setcookie('remember_token', $token, $tokens[$hash]['expires'], '/', '', true, true);
}
header('Location: dashboard.php');
exit;
}
}
// Verificar cookie em requisições sem sessão
if (!isset($_SESSION['user_id']) && isset($_COOKIE['remember_token'])) {
$hash = hash('sha256', $_COOKIE['remember_token']);
if (isset($tokens[$hash]) && $tokens[$hash]['expires'] > time()) {
$_SESSION['user_id'] = $tokens[$hash]['user_id'];
session_regenerate_id(true);
}
}
?>
<?php
session_start();
if (isset($_COOKIE['remember_token'])) {
$hash = hash('sha256', $_COOKIE['remember_token']);
// deletar do banco
setcookie('remember_token', '', time()-3600, '/', '', true, true);
}
$_SESSION = [];
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time()-42000, $params["path"], $params["domain"], $params["secure"], $params["httponly"]);
}
session_destroy();
header('Location: login.php');
exit;
function verificar_login() {
session_start();
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
}
// No topo de cada página protegida:
verificar_login();
<?php
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'] ?? '')) {
die('Token CSRF inválido.');
}
// processar login
}
?>
<form method="post">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<!-- outros campos -->
</form>
Boas práticas e observações finais
Além do que vimos, é fundamental manter o código organizado e testado. Considere usar um sistema de gerenciamento de dependências (Composer) e bibliotecas como defuse/php-encryption para criptografia, se necessário. Lembre-se de que a segurança é um processo contínuo: mantenha o PHP e as bibliotecas atualizadas, monitore logs e esteja atento a novas vulnerabilidades.
Nesta aula, você aprendeu a implementar autenticação com sessões, lembrar-me e logout seguro. Pratique os exercícios e adapte-os para seu projeto real, sempre priorizando a segurança.