Situação atual: PHP2Shell. Duas vulnerabilidades críticas do PHP (CVSS 9,2) permitem executar código malicioso sem autenticação. Patches disponíveis desde 21/10/2026.O que fazer agora →

Proteger sessões em PHP: segurança de sessões PHP na prática

As sessões mantêm os utilizadores autenticados e são, por isso, um alvo apetecível. Como configurar com segurança cookies, IDs de sessão e locais de armazenamento e aplicar o aviso do PHP2Shell sobre o upload progress.

Atualizado em 6 min de leituraGuias

Em resumo: na segurança de sessões PHP contam quatro coisas: definições de cookies seguras, um novo ID de sessão após cada autenticação, locais de armazenamento separados e protegidos e durações curtas. Por causa do PHP2Shell, o session.upload_progress.enabled=0 deve ainda estar definido no servidor, enquanto não houver razão para esta função.

Porque é que as sessões são um alvo apetecível

Uma sessão memoriza que um utilizador está autenticado. Tecnicamente, o navegador recebe para isso um ID de sessão aleatório num cookie, e o servidor guarda os dados correspondentes. Quem conhecer este ID é, para a aplicação, o mesmo utilizador, sem palavra-passe e sem segundo fator.

Para os operadores, isto significa: uma gestão de sessões fraca pode anular a proteção do login, mesmo que as palavras-passe e a autenticação de dois fatores estejam bem implementadas. É especialmente crítico em áreas de administração, contas de clientes e portais com dados pessoais.

Os riscos típicos são a captura do ID de sessão (session hijacking), a introdução de um ID conhecido antes do login (session fixation) e sessões válidas durante demasiado tempo em computadores partilhados.

Definições de base seguras no php.ini

Os valores mais importantes podem ser definidos centralmente no php.ini ou, por site, no pool FPM. No pool, utilize php_admin_value ou php_admin_flag, para que a aplicação não possa substituir os valores.

; Reforço de base das sessões
session.use_strict_mode   = 1
session.use_only_cookies  = 1
session.use_trans_sid     = 0
session.cookie_httponly   = 1
session.cookie_secure     = 1
session.cookie_samesite   = "Lax"
session.cookie_lifetime   = 0
session.gc_maxlifetime    = 1800
session.name              = "APPSESSID"

; Aviso do PHP2Shell do php.net, depois reiniciar todos os workers
session.upload_progress.enabled = 0

O use_strict_mode faz com que o PHP não aceite IDs desconhecidos impostos pelo navegador. O cookie_httponly impede que o JavaScript leia o ID, o que limita as consequências do Cross-Site Scripting. O cookie_secure envia o cookie apenas por HTTPS. O SameSite=Lax dificulta pedidos cross-site com sessão válida; para áreas puramente administrativas, o Strict pode ser adequado.

A duração de 1800 segundos é um valor de exemplo. Defina-a segundo a necessidade de proteção: mais curta para administração e dados de clientes, mais longa apenas onde for tecnicamente necessário.

Tenha em conta que o gc_maxlifetime apenas determina a partir de quando os dados de sessão antigos podem ser apagados. Em algumas distribuições, quem limpa os ficheiros de sessão não é o próprio PHP, mas uma tarefa regular do sistema. Verifique então que valor essa tarefa utiliza, para que a sua definição produza realmente efeito. Para terminar a sessão por inatividade, não confie além disso apenas na função de limpeza, mas verifique na própria aplicação o momento da última ação, como mostrado no exemplo seguinte.

Renovar o ID de sessão após o login

A regra mais importante no código é gerar um novo ID de sessão em cada mudança de permissão, ou seja, no login, na mudança para um modo de administração e no logout. O exemplo seguinte mostra um padrão de base limpo com password_verify():

<?php
declare(strict_types=1);

session_start();

function anmelden(PDO $db, string $email, string $passwort): bool
{
    $stmt = $db->prepare('SELECT id, passwort_hash FROM benutzer WHERE email = :email');
    $stmt->execute(['email' => $email]);
    $nutzer = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$nutzer || !password_verify($passwort, $nutzer['passwort_hash'])) {
        return false; // mesma mensagem para endereço errado e palavra-passe errada
    }

    session_regenerate_id(true);           // novo ID, descartar o antigo
    $_SESSION['benutzer_id']   = (int) $nutzer['id'];
    $_SESSION['letzte_aktion'] = time();
    return true;
}

function pruefeLeerlauf(int $sekunden = 900): void
{
    if (isset($_SESSION['letzte_aktion']) && time() - $_SESSION['letzte_aktion'] > $sekunden) {
        abmelden();
        return;
    }
    $_SESSION['letzte_aktion'] = time();
}

function abmelden(): void
{
    $_SESSION = [];
    $p = session_get_cookie_params();
    setcookie(session_name(), '', [
        'expires'  => time() - 3600,
        'path'     => $p['path'],
        'domain'   => $p['domain'],
        'secure'   => $p['secure'],
        'httponly' => $p['httponly'],
        'samesite' => $p['samesite'],
    ]);
    session_destroy();
}

Em complemento, proteja os formulários que alteram algo com um token CSRF. É gerado com bin2hex(random_bytes(32)), guardado na sessão e comparado com hash_equals() no envio.

Locais de armazenamento e separação de clientes

Por defeito, o PHP guarda as sessões como ficheiros num diretório partilhado. Num servidor com vários sites, isso pode significar que um site comprometido consiga ler as sessões de outro. Por isso, cada site deve ter um pool próprio, com utilizador próprio e caminho de sessões próprio.

; no pool FPM do respetivo site
php_admin_value[session.save_path] = /var/lib/php/sessions/kunde-a

O diretório deve pertencer ao utilizador do pool e ter as permissões 0700. Como isolar ainda mais os sites uns dos outros, descreve-o o guia open_basedir e isolamento por site. Se as sessões forem guardadas em Redis ou numa base de dados, aplicam-se os mesmos princípios: acesso próprio por aplicação, sem acesso de rede aberto.

Segurança de sessões PHP e PHP2Shell

As vulnerabilidades do PHP2Shell CVE-2026-109618 e CVE-2026-109619 estão no próprio PHP. Como uma das medidas transitórias, o php.net indica definir session.upload_progress.enabled=0 no php.ini, no pool FPM ou na configuração do servidor e reiniciar depois todos os workers. Expressamente não através de ini_set() nem de um .user.ini.

A 23/10/2026 seguiu-se um aviso sobre o CVE-2026-115560. Uma possível execução de código só existe aí com uma combinação invulgar de serialização de sessões, mudança por ini_set() antes do session_start() e upload progress ativo. A mitigação é a mesma definição, até à próxima versão regular de correção de erros.

Importante

Esta definição é uma medida transitória e não uma proteção completa. Decisivo continua a ser a atualização para uma versão corrigida do PHP e o posterior reinício de todos os workers PHP.

Verifique, após a alteração, que valor vale realmente. A linha de comandos mostra muitas vezes uma configuração diferente da do servidor web; é conclusiva uma página de teste protegida e depois apagada de novo, ou a própria configuração do pool.

Erros típicos nas sessões de aplicações PHP

Muitas fragilidades surgem não por falta de definições, mas pela forma como as aplicações utilizam as sessões. Deve perguntar de forma orientada por estes padrões no código próprio e junto de prestadores de serviços:

  • Demasiado conteúdo na sessão: palavras-passe, dados de pagamento completos ou grandes conjuntos de dados não pertencem à sessão. Guarde apenas o necessário para a associação, por exemplo o ID de utilizador e as funções.
  • Permissões apenas a partir da sessão: quem é administrador deve ser verificado na base de dados em ações críticas. Caso contrário, permissões retiradas continuam em vigor até ao fim da sessão.
  • ID de sessão no endereço: os IDs nos URLs acabam em registos, históricos do navegador e indicações de referer. use_only_cookies e use_trans_sid = 0 impedem-no.
  • Sem fim da sessão: sem um timeout de inatividade, um administrador num computador partilhado permanece autenticado sem limite.
  • HTTPS mal detetado atrás de um proxy: se a cifragem terminar no balanceador de carga, a aplicação tem de o saber. Caso contrário, pode definir cookies sem a flag Secure.

Após um incidente de segurança, faz parte também descartar todas as sessões existentes. O mais simples é apagar o conteúdo do diretório de sessões ou alterar o nome da sessão. Todos os utilizadores têm então de se autenticar de novo, e as sessões roubadas deixam de ter valor.

Como ajuda concretamente a PHP SOS?

  • Verificamos as definições de sessão, as flags de cookies e os locais de armazenamento em todos os sites de um servidor e documentamos os valores realmente eficazes.
  • Aplicamos as medidas transitórias do PHP2Shell por pool, reiniciamos os workers de forma controlada e comprovamos o resultado.
  • Separamos os sites com pools e caminhos de sessões próprios no âmbito do reforço de segurança do servidor.
  • Para desenvolvimentos próprios, verificamos no código o login, o logout e a mudança de sessão e recomendamos alterações concretas.

Perguntas frequentes

O que é o session hijacking?

No session hijacking, um atacante assume a sessão de um utilizador autenticado, obtendo o seu ID de sessão. Com o ID, é esse utilizador para a aplicação, sem conhecer a palavra-passe. Flags de cookies seguras e durações curtas dificultam isto claramente.

Porque é que o ID de sessão tem de ser renovado após o login?

Se o ID se mantiver igual antes e depois do login, um ID conhecido de antemão ou introduzido por terceiros poderia continuar válido após a autenticação. O session_regenerate_id(true) gera um novo ID e descarta o antigo.

Posso desativar o session.upload_progress.enabled com ini_set()?

Não, segundo o php.net isso não basta. A definição pertence ao php.ini, ao pool FPM ou à configuração do servidor, não ao ini_set() nem a um .user.ini. Depois, todos os workers PHP têm de ser reiniciados.

Onde devem ser guardados os ficheiros de sessão?

Num diretório fora da raiz web, que só o utilizador do respetivo pool PHP possa ler. Em servidores com vários sites, cada site deve ter o seu próprio diretório de sessões.

Fontes

Contacto imediato

Cada hora conta. Escreva-nos agora.

Descreva brevemente o sistema, a versão do PHP e o que observou. Entramos em contacto consigo, esclarecemos a situação e trabalhamos remotamente até o incidente estar concluído e documentado.

Ajuda imediata