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 →

Evitar XSS em PHP: combater o Cross-Site Scripting com segurança

Como os programadores evitam o Cross-Site Scripting em aplicações PHP: codificação adequada ao contexto, escaping automático em templates, limpeza de HTML e uma Content Security Policy.

Atualizado em 6 min de leituraGuias

Em resumo: evita o XSS em PHP codificando cada saída de acordo com o seu contexto: htmlspecialchars() para HTML, json_encode() com opções de proteção para JavaScript, URLs verificados para ligações. Os templates com escaping automático tiram-lhe grande parte desse trabalho. Uma Content Security Policy e cookies HttpOnly limitam o dano, caso ainda reste uma vulnerabilidade.

Porque é que o Cross-Site Scripting deve agora ser verificado também

Em outubro de 2026, a atenção está no PHP2Shell, duas vulnerabilidades críticas no interpretador PHP, para as quais há patches desde 21/10/2026. Muitas empresas mandam, por essa razão, verificar as suas aplicações web. É uma boa ocasião para olhar também para fragilidades no código próprio, que nenhuma atualização do PHP corrige.

O Cross-Site Scripting, abreviadamente XSS, é uma delas. O OWASP Top 10 inclui o XSS na categoria Injection. Ao contrário de uma vulnerabilidade no servidor, o ataque dirige-se aqui aos visitantes e utilizadores do seu site, por exemplo aos clientes na loja ou aos colaboradores no backend.

As consequências podem ser consideráveis: sessões de administradores assumidas, formulários manipulados que enviam dados a terceiros ou conteúdos falsificados no seu site. Se uma conta de administrador for assumida, isso pode levar ao controlo total da aplicação.

Como surge o XSS em aplicações PHP?

O XSS surge quando uma aplicação envia para uma página, sem verificação, dados de uma fonte não fiável. O navegador já não consegue distinguir entre os seus conteúdos e o código introduzido e executa ambos.

Distinguem-se três formas. No XSS refletido, o conteúdo provém diretamente do pedido atual, por exemplo de um termo de pesquisa no URL. No XSS armazenado, está na base de dados, por exemplo num comentário ou num campo de perfil, e atinge todos os que abrem a página. O XSS baseado em DOM surge no JavaScript do navegador, quando os scripts escrevem na página valores vindos do URL.

Não são fiáveis todos os dados que não constam fixos do código: campos de formulário, parâmetros de URL, cookies, nomes de ficheiros, respostas de interfaces externas e conteúdos da própria base de dados que, em algum momento, vieram do exterior.

Codificar corretamente a saída conforme o contexto

A regra mais importante é: codificar na saída, e de acordo com o local onde o valor vai parar. Para conteúdo HTML e atributos entre aspas, o htmlspecialchars() é o meio indicado. Uma pequena função auxiliar evita que se esqueçam opções:

<?php
function e(?string $wert): string
{
    return htmlspecialchars($wert ?? '', ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8');
}
?>
<h2><?= e($produkt['name']) ?></h2>
<input type="text" name="suche" value="<?= e($_GET['q'] ?? '') ?>">

Outros contextos precisam de outros meios:

Contexto Método seguro
Conteúdo HTML, atributo entre aspas htmlspecialchars() com ENT_QUOTES e UTF-8
Dados para JavaScript json_encode() com JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_HEX_QUOT
Parâmetro num URL rawurlencode() para o valor individual
URL completo a partir de entrada do utilizador verificar o esquema, admitir apenas https e http, depois htmlspecialchars()
Atributos sem aspas, manipuladores de eventos, CSS evitar, não inserir valores aí

Para dados de que um script precisa, um atributo de dados ou um bloco JSON é geralmente o caminho mais limpo:

<?php
$daten = json_encode($konfiguration, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
?>
<div id="app" data-konfig="<?= e($daten) ?>"></div>

No JavaScript, lê depois o valor através do atributo de dados e define conteúdos com textContent em vez de innerHTML.

Templates, limpeza de HTML e frameworks

Os motores de templates como o Twig e o Blade codificam as saídas automaticamente por defeito. Isto exclui grande parte dos erros típicos. O perigo surge onde este comportamento é desativado de propósito, por exemplo com o filtro raw no Twig ou a sintaxe {!! !!} no Blade. Tais pontos devem ser raros no código e verificados de forma orientada em cada revisão.

Se a sua aplicação deve permitir deliberadamente HTML dos utilizadores, por exemplo formatação em comentários ou num sistema editorial, codificar não basta, porque o HTML deve ser apresentado. Precisa então de uma biblioteca de limpeza testada, como o HTML Purifier, que deixe passar apenas uma lista fixa de elementos e atributos permitidos. Os filtros próprios com expressões regulares revelam-se, pela experiência, lacunares.

Importante

Verifique sobretudo as áreas de backend. O XSS em vistas que só os administradores veem é muitas vezes subestimado, mas atinge precisamente as contas com mais permissões.

Encontrar XSS no código PHP existente

Em aplicações crescidas, a primeira pergunta não é, em regra, como codificar corretamente, mas onde isso foi esquecido até agora. Um começo pragmático é procurar os pontos em que as variáveis são enviadas diretamente para a saída:

# Saídas diretas de dados do pedido
grep -rnE "(echo|print|<\?=)\s*\\\$_(GET|POST|REQUEST|COOKIE|SERVER)" src/ templates/ --include="*.php"
# Escaping automático desativado no Twig e no Blade
grep -rnE "\|raw|\{!!" templates/ resources/views/

Os resultados são tarefas de verificação, não conclusões prontas. O XSS armazenado só é encontrado em parte por tal pesquisa, porque os valores vêm aí da base de dados. Verifique por isso adicionalmente todas as vistas que mostram conteúdos de utilizadores: comentários, perfis, notas de encomenda, tickets de suporte. Nos CMS e lojas, os plugins e temas de terceiros incluem-se expressamente, pois é aí que está grande parte da lógica de saída.

Segunda linha de defesa: CSP e cookies seguros

Mesmo com código cuidadoso, um ponto pode ser esquecido. Duas medidas limitam então o dano. Uma Content Security Policy permite ao navegador apenas scripts de fontes definidas e proíbe scripts incorporados, desde que não estejam expressamente autorizados:

Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"

Introduza uma política destas por fases, primeiro no modo de simples relatório através de Content-Security-Policy-Report-Only, porque as integrações existentes de serviços de análise ou de pagamento têm de ser, em regra, acrescentadas. Outros cabeçalhos são descritos no guia Cabeçalhos de segurança para sites.

Adicionalmente, os cookies de sessão devem ter o atributo HttpOnly, para que os scripts não os possam ler. Como proteger as sessões no seu conjunto, está no guia Proteger sessões em PHP. Uma fragilidade afim, do lado da base de dados, é tratada em Evitar SQL Injection em PHP.

Como ajuda concretamente a PHP SOS?

  • Verificamos os templates e as saídas da sua aplicação quanto a codificação em falta ou errada.
  • Avaliamos os achados segundo a acessibilidade e as contas afetadas e mostramos correções concretas.
  • Introduzimos uma Content Security Policy por fases, sem perturbar as funções da loja ou de pagamento.
  • Voltamos a testar após a correção e documentamos o resultado.

Uma verificação independente é descrita no serviço Auditoria de segurança e teste de intrusão.

Perguntas frequentes

O htmlspecialchars basta contra o XSS?

Para texto normal no conteúdo HTML e em atributos entre aspas, sim, se indicar ENT_QUOTES e o conjunto de caracteres UTF-8. Em JavaScript, em URLs ou em CSS aplicam-se outras regras, e aí é necessária uma codificação própria e adequada.

Devo filtrar as entradas ao guardar ou na saída?

Codifique na saída, de acordo com o destino. As entradas devem ser verificadas quanto à plausibilidade na receção, por exemplo comprimento e formato. Quem filtra apenas ao guardar ignora dados de outras fontes e dificulta a utilização posterior noutros contextos.

De que serve uma Content Security Policy?

Uma Content Security Policy define de que fontes o navegador pode carregar e executar scripts. Pode limitar claramente os efeitos de uma vulnerabilidade XSS que tenha passado despercebida, mas não substitui uma codificação correta.

Como permito formatação como negrito ou ligações em comentários?

Utilize uma biblioteca de limpeza de HTML testada, com uma lista de permissão de elementos e atributos autorizados. As expressões regulares próprias para remover scripts são propensas a erros e não devem ser usadas.

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