Dominando Sessões e Cookies: Autenticação Stateful em PHP em Projetos Reais Já leu

Entendendo Sessões e Cookies em PHP Sessões e cookies são os pilares da autenticação stateful na web. Um cookie é um pequeno arquivo armazenado no navegador do cliente que persiste entre requisições, enquanto uma sessão é um mecanismo servidor-side que mantém dados do usuário em memória ou banco de dados. A diferença crucial: cookies são visíveis e modificáveis pelo cliente; sessões são seguras, pois residem no servidor. Em PHP, quando você cria uma sessão, um identificador único (Session ID) é gerado e armazenado em um cookie no navegador. A cada requisição, esse cookie é enviado automaticamente, permitindo que o servidor recupere os dados da sessão correspondente. Esse padrão é chamado autenticação stateful — o servidor mantém o estado da autenticação. Implementando Sessões Seguras Iniciando e Usando Sessões Esses parâmetros são essenciais: impede ataques XSS (Cross-Site Scripting), força HTTPS, e previne CSRF (Cross-Site Request Forgery). Nunca ignore essas configurações em produção. Autenticação com Hash de Senha Nunca armazene senhas em texto

Entendendo Sessões e Cookies em PHP

Sessões e cookies são os pilares da autenticação stateful na web. Um cookie é um pequeno arquivo armazenado no navegador do cliente que persiste entre requisições, enquanto uma sessão é um mecanismo servidor-side que mantém dados do usuário em memória ou banco de dados. A diferença crucial: cookies são visíveis e modificáveis pelo cliente; sessões são seguras, pois residem no servidor.

Em PHP, quando você cria uma sessão, um identificador único (Session ID) é gerado e armazenado em um cookie no navegador. A cada requisição, esse cookie é enviado automaticamente, permitindo que o servidor recupere os dados da sessão correspondente. Esse padrão é chamado autenticação stateful — o servidor mantém o estado da autenticação.

Implementando Sessões Seguras

Iniciando e Usando Sessões

<?php
// Sempre no início do arquivo, antes de qualquer output
session_start();

// Configurações de segurança (PHP 7.1+)
session_set_cookie_params([
    'lifetime' => 3600,           // 1 hora
    'path' => '/',
    'domain' => 'exemplo.com',
    'secure' => true,             // HTTPS apenas
    'httponly' => true,           // Sem acesso via JavaScript
    'samesite' => 'Strict'        // Proteção contra CSRF
]);

// Verificar se o usuário está autenticado
if (!isset($_SESSION['user_id'])) {
    header('Location: login.php');
    exit;
}

echo "Bem-vindo, " . htmlspecialchars($_SESSION['username']);
?>

Esses parâmetros são essenciais: httponly impede ataques XSS (Cross-Site Scripting), secure força HTTPS, e samesite previne CSRF (Cross-Site Request Forgery). Nunca ignore essas configurações em produção.

Autenticação com Hash de Senha

<?php
session_start();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = $_POST['username'] ?? '';
    $password = $_POST['password'] ?? '';

    // Simular busca no banco de dados
    $user = [
        'id' => 1,
        'username' => 'joao',
        'password_hash' => password_hash('senha123', PASSWORD_BCRYPT)
    ];

    if ($username === $user['username'] && 
        password_verify($password, $user['password_hash'])) {

        $_SESSION['user_id'] = $user['id'];
        $_SESSION['username'] = $user['username'];
        $_SESSION['created_at'] = time();

        header('Location: dashboard.php');
        exit;
    } else {
        $error = "Credenciais inválidas";
    }
}
?>

<form method="POST">
    <input type="text" name="username" required>
    <input type="password" name="password" required>
    <button type="submit">Entrar</button>
</form>
<?php if (isset($error)): ?>
    <p><?php echo htmlspecialchars($error); ?></p>
<?php endif; ?>

Nunca armazene senhas em texto plano. Use password_hash() com o algoritmo BCRYPT para hash e password_verify() para validação. Isso é o padrão ouro de segurança.

Trabalhando com Cookies Explicitamente

Quando Usar Cookies ao Invés de Sessões

Cookies são ideais para dados não-sensíveis que devem persistir além da sessão. Um exemplo comum: preferências de idioma ou tema visual. Nunca coloque IDs de usuário ou tokens diretamente em cookies sem proteção.

<?php
// Definir um cookie seguro
setcookie(
    'idioma',                    // nome
    'pt-BR',                     // valor
    [
        'expires' => time() + (365 * 24 * 60 * 60), // 1 ano
        'path' => '/',
        'secure' => true,
        'httponly' => true,
        'samesite' => 'Lax'
    ]
);

// Acessar cookie
$idioma = $_COOKIE['idioma'] ?? 'pt-BR';

// Deletar cookie
setcookie('idioma', '', ['expires' => time() - 3600]);
?>

Note que setcookie() deve ser chamado antes de qualquer output. Use a sintaxe de array para setcookie() (PHP 7.3+), pois é mais legível e segura.

Segurança e Manutenção de Sessões

Regeneração de Session ID

Um ataque comum é a fixação de sessão. Um atacante tenta forçar o usuário a usar um Session ID conhecido. A defesa é regenerar o ID após login:

<?php
session_start();

// Após validar credenciais (no login)
session_regenerate_id(true);  // true = destruir sessão antiga
$_SESSION['user_id'] = $user['id'];

// Validar idade da sessão (logout automático)
$tempo_inatividade = 30 * 60; // 30 minutos
$tempo_atual = time();

if (isset($_SESSION['created_at'])) {
    if ($tempo_atual - $_SESSION['created_at'] > $tempo_inatividade) {
        session_destroy();
        header('Location: login.php?timeout=1');
        exit;
    }
}

// Logout
if (isset($_GET['logout'])) {
    session_destroy();
    header('Location: index.php');
    exit;
}
?>

A regeneração de ID impede ataques de fixação. O timeout automático protege contra sessões abandonadas em computadores públicos — uma prática essencial.

Armazenamento Seguro de Sessão

Por padrão, PHP armazena sessões em arquivos. Para aplicações grandes ou com múltiplos servidores, use banco de dados:

<?php
session_start();

class SessionHandler extends SessionHandlerInterface {
    private $pdo;

    public function __construct(\PDO $pdo) {
        $this->pdo = $pdo;
    }

    public function read($id) {
        $stmt = $this->pdo->prepare(
            'SELECT data FROM sessions WHERE id = ? AND expires > ?'
        );
        $stmt->execute([$id, time()]);
        $result = $stmt->fetch();
        return $result ? $result['data'] : '';
    }

    public function write($id, $data) {
        $stmt = $this->pdo->prepare(
            'INSERT INTO sessions (id, data, expires) VALUES (?, ?, ?) 
             ON DUPLICATE KEY UPDATE data = ?, expires = ?'
        );
        return $stmt->execute([$id, $data, time() + 3600, $data, time() + 3600]);
    }

    public function destroy($id) {
        return $this->pdo->prepare('DELETE FROM sessions WHERE id = ?')
                  ->execute([$id]);
    }

    // Outros métodos obrigatórios...
    public function open($path, $name) { return true; }
    public function close() { return true; }
    public function gc($max_lifetime) { 
        return $this->pdo->prepare('DELETE FROM sessions WHERE expires < ?')
                 ->execute([time()]);
    }
}

// Usar o handler customizado
$handler = new SessionHandler($pdo);
session_set_save_handler($handler);
session_start();
?>

Banco de dados oferece melhor controle, escalabilidade e facilita limpeza automática de sessões expiradas.

Conclusão

Sessões e cookies formam a base da autenticação web. O aprendizado principal: use sessões para dados sensíveis, proteja com HTTPS, regenere IDs e defina timeouts. Cookies são complementares para dados não-críticos. Sempre implemente httponly, secure e samesite para mitigar os principais vetores de ataque (XSS, CSRF, man-in-the-middle). Em produção, armazene sessões em banco de dados e monitore expiração.

Referências


Artigos relacionados