Sessions e cookies no PHP

1. Fundamentos de Cookies no PHP

1.1. O que são cookies e como funcionam no protocolo HTTP

Cookies são pequenos arquivos de texto armazenados no navegador do cliente, enviados pelo servidor através do cabeçalho HTTP Set-Cookie. No PHP, os cookies permitem manter estado entre requisições HTTP, que por natureza são stateless. Cada cookie contém um par nome-valor, além de metadados como domínio, path e data de expiração.

A função setcookie() define um cookie que será enviado ao navegador. O array superglobal $_COOKIE permite acessar os cookies recebidos na requisição atual.

<?php
// Criando um cookie que expira em 1 hora
setcookie('usuario', 'joao', time() + 3600, '/');

// Lendo o cookie na próxima requisição
if (isset($_COOKIE['usuario'])) {
    echo "Bem-vindo de volta, " . htmlspecialchars($_COOKIE['usuario']);
}
?>

1.3. Parâmetros essenciais: expiração, path, domínio e secure/httponly

<?php
setcookie(
    'preferencia_tema',  // nome
    'escuro',            // valor
    time() + 86400 * 30, // expira em 30 dias
    '/app/',             // path - disponível apenas em /app/
    'meusite.com',       // domínio
    true,                // secure - apenas HTTPS
    true                 // httponly - inacessível via JavaScript
);
?>

2. Manipulação Avançada de Cookies

2.1. Cookies com arrays e serialização de dados

<?php
// Armazenando array serializado
$preferencias = [
    'idioma' => 'pt-BR',
    'fuso' => 'America/Sao_Paulo',
    'notificacoes' => true
];
setcookie('preferencias', serialize($preferencias), time() + 86400);

// Recuperando e desserializando
if (isset($_COOKIE['preferencias'])) {
    $dados = unserialize($_COOKIE['preferencias']);
    echo "Idioma: " . $dados['idioma'];
}
?>

2.2. Removendo e atualizando cookies existentes

<?php
// Removendo cookie (expiração no passado)
setcookie('usuario', '', time() - 3600, '/');

// Atualizando valor
setcookie('preferencia_tema', 'claro', time() + 86400, '/');
?>

2.3. Boas práticas de segurança: SameSite, HttpOnly e Secure flags

<?php
// Cookie seguro com SameSite Strict
setcookie(
    'token_sessao',
    $token,
    [
        'expires' => time() + 3600,
        'path' => '/',
        'domain' => 'meusite.com',
        'secure' => true,
        'httponly' => true,
        'samesite' => 'Strict' // ou 'Lax'
    ]
);
?>

3. Introdução às Sessions no PHP

3.1. Conceito de sessão e gerenciamento de estado stateless

Sessões no PHP permitem armazenar dados no servidor entre requisições de um mesmo usuário. Diferente dos cookies, os dados da sessão ficam no servidor, e apenas um identificador único (session ID) é enviado ao cliente, normalmente via cookie.

3.2. Iniciando e configurando sessões com session_start()

<?php
// Deve ser chamado antes de qualquer saída HTML
session_start();

// Configurando parâmetros antes de iniciar
ini_set('session.use_strict_mode', 1);
session_set_cookie_params([
    'lifetime' => 3600,
    'path' => '/',
    'domain' => 'meusite.com',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Lax'
]);
session_start();
?>

3.3. Armazenamento e recuperação de dados via $_SESSION

<?php
session_start();

// Armazenando dados
$_SESSION['usuario_id'] = 42;
$_SESSION['ultimo_acesso'] = time();

// Recuperando dados
if (isset($_SESSION['usuario_id'])) {
    echo "Usuário logado: ID " . $_SESSION['usuario_id'];
}
?>

4. Configuração e Personalização de Sessions

4.1. Configurações no php.ini: session.save_path, session.gc_maxlifetime

; php.ini
session.save_path = "/var/lib/php/sessions"
session.gc_maxlifetime = 1440    ; 24 minutos
session.gc_probability = 1
session.gc_divisor = 100
session.cookie_lifetime = 0      ; até o navegador fechar

4.2. Manipuladores de armazenamento: arquivos, banco de dados e Redis

<?php
// Usando Redis para armazenamento de sessão
ini_set('session.save_handler', 'redis');
ini_set('session.save_path', 'tcp://127.0.0.1:6379');
session_start();
?>

4.3. Sessões personalizadas com session_set_save_handler()

<?php
class SessionHandlerPersonalizado implements SessionHandlerInterface {
    private $pdo;

    public function open($savePath, $sessionName): bool {
        $this->pdo = new PDO('mysql:host=localhost;dbname=sessoes', 'user', 'pass');
        return true;
    }

    public function read($sessionId): string {
        $stmt = $this->pdo->prepare('SELECT dados FROM sessoes WHERE id = ?');
        $stmt->execute([$sessionId]);
        return $stmt->fetchColumn() ?: '';
    }

    public function write($sessionId, $data): bool {
        $stmt = $this->pdo->prepare(
            'REPLACE INTO sessoes (id, dados, ultimo_acesso) VALUES (?, ?, NOW())'
        );
        return $stmt->execute([$sessionId, $data]);
    }

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

    public function close(): bool { return true; }

    public function gc($maxlifetime): int {
        $stmt = $this->pdo->prepare(
            'DELETE FROM sessoes WHERE ultimo_acesso < DATE_SUB(NOW(), INTERVAL ? SECOND)'
        );
        $stmt->execute([$maxlifetime]);
        return $stmt->rowCount();
    }
}

session_set_save_handler(new SessionHandlerPersonalizado(), true);
session_start();
?>

5. Ciclo de Vida e Gerenciamento de Sessão

5.1. Regeneração de ID de sessão com session_regenerate_id()

<?php
session_start();

// Após login bem-sucedido, regenerar ID para prevenir fixation
if ($login_sucesso) {
    session_regenerate_id(true); // true = deleta sessão antiga
    $_SESSION['autenticado'] = true;
}
?>

5.2. Destruição de sessão: session_destroy() e limpeza de cookies

<?php
session_start();

// Limpar dados da sessão
$_SESSION = [];

// Destruir a sessão no servidor
session_destroy();

// Remover o cookie da sessão
setcookie(session_name(), '', time() - 3600, '/');
?>

5.3. Controle de tempo de vida e garbage collection

<?php
// Verificar expiração manual
session_start();
$tempo_maximo = 1800; // 30 minutos

if (isset($_SESSION['ultimo_acesso']) && 
    (time() - $_SESSION['ultimo_acesso']) > $tempo_maximo) {
    session_destroy();
    header('Location: login.php');
    exit;
}

$_SESSION['ultimo_acesso'] = time();
?>

6. Segurança em Sessions e Cookies

6.1. Proteção contra session fixation e session hijacking

<?php
session_start();

// Verificar se o ID foi gerado pelo servidor
if (!isset($_SESSION['initiated'])) {
    session_regenerate_id(true);
    $_SESSION['initiated'] = true;
}

// Validar fingerprint do navegador
if (!isset($_SESSION['fingerprint'])) {
    $_SESSION['fingerprint'] = md5($_SERVER['HTTP_USER_AGENT'] . $_SERVER['REMOTE_ADDR']);
} elseif ($_SESSION['fingerprint'] !== md5($_SERVER['HTTP_USER_AGENT'] . $_SERVER['REMOTE_ADDR'])) {
    session_destroy();
    exit('Sessão inválida');
}
?>

6.2. Validação de User-Agent e IP para mitigar sequestro

<?php
session_start();

$ip_atual = $_SERVER['REMOTE_ADDR'];
$agent_atual = $_SERVER['HTTP_USER_AGENT'];

if (!isset($_SESSION['ip_cliente'])) {
    $_SESSION['ip_cliente'] = $ip_atual;
    $_SESSION['user_agent'] = $agent_atual;
} elseif ($_SESSION['ip_cliente'] !== $ip_atual || 
          $_SESSION['user_agent'] !== $agent_atual) {
    // Possível hijacking - invalidar sessão
    session_destroy();
    header('Location: login.php?erro=seguranca');
    exit;
}
?>

6.3. Criptografia de dados sensíveis em sessões e cookies

<?php
// Criptografar dados antes de armazenar na sessão
$dados_sensiveis = ['cartao' => '4111111111111111', 'cvv' => '123'];
$chave = 'minha_chave_secreta_32bytes!';
$iv = openssl_random_pseudo_bytes(16);

$dados_criptografados = openssl_encrypt(
    serialize($dados_sensiveis),
    'aes-256-cbc',
    $chave,
    0,
    $iv
);

$_SESSION['dados_pagamento'] = base64_encode($iv . $dados_criptografados);
?>

7. Integração entre Sessions e Cookies

7.1. Como o PHP usa cookies para gerenciar IDs de sessão

Por padrão, o PHP utiliza um cookie chamado PHPSESSID para armazenar o ID da sessão no navegador. Esse cookie é do tipo session (expira ao fechar o navegador) e é enviado automaticamente em cada requisição.

7.2. Sessões sem cookies: transmissão via URL (SID)

<?php
ini_set('session.use_cookies', 0);
ini_set('session.use_only_cookies', 0);
ini_set('session.use_trans_sid', 1);

session_start();
$_SESSION['dado'] = 'valor';

// O PHP automaticamente adiciona ?PHPSESSID=X às URLs relativas
echo '<a href="pagina2.php">Próxima página</a>';
?>

7.3. Cache de sessão e impacto no desempenho com cookies

<?php
// Configurar cache para sessões
ini_set('session.cache_limiter', 'private');
ini_set('session.cache_expire', 180); // 3 minutos

session_start();

// Evitar cache de páginas com sessão
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Pragma: no-cache');
?>

8. Casos Práticos e Boas Práticas

8.1. Implementação de login persistente com cookies e sessões

<?php
session_start();

function login($usuario_id, $lembrar = false) {
    $_SESSION['usuario_id'] = $usuario_id;

    if ($lembrar) {
        $token = bin2hex(random_bytes(32));
        setcookie('remember_token', $token, time() + 86400 * 30, '/', '', true, true);

        // Armazenar hash do token no banco
        $hash = password_hash($token, PASSWORD_DEFAULT);
        // INSERT INTO tokens (usuario_id, hash) VALUES (?, ?)
    }

    session_regenerate_id(true);
}

function verificar_login() {
    if (isset($_SESSION['usuario_id'])) {
        return $_SESSION['usuario_id'];
    }

    if (isset($_COOKIE['remember_token'])) {
        // Verificar token no banco
        // SELECT usuario_id FROM tokens WHERE hash = ?
        // Se válido, recriar sessão
    }

    return false;
}
?>

8.2. Carrinho de compras: combinando sessão e cookies

<?php
session_start();

class Carrinho {
    public function adicionarItem($produto_id, $quantidade) {
        if (!isset($_SESSION['carrinho'])) {
            $_SESSION['carrinho'] = [];
        }

        if (isset($_SESSION['carrinho'][$produto_id])) {
            $_SESSION['carrinho'][$produto_id] += $quantidade;
        } else {
            $_SESSION['carrinho'][$produto_id] = $quantidade;
        }

        // Cookie para lembrar carrinho por 7 dias
        setcookie('carrinho_abandonado', '1', time() + 86400 * 7, '/');
    }

    public function totalItens() {
        return array_sum($_SESSION['carrinho'] ?? []);
    }
}
?>

8.3. Debugging: logando sessões e cookies com error_log()

<?php
session_start();

function debug_sessao() {
    $info = [
        'session_id' => session_id(),
        'session_name' => session_name(),
        'session_status' => session_status(),
        'session_data' => $_SESSION,
        'cookie_params' => session_get_cookie_params(),
        'cookie_recebido' => $_COOKIE
    ];

    error_log('DEBUG SESSÃO: ' . print_r($info, true));
}

debug_sessao();
?>

Referências