Prevenindo XSS
Esta aula aborda a prevenção de Cross-Site Scripting (XSS) em aplicações PHP, explicando o que é XSS, como usar htmlspecialchars para escapar saídas, a importância de escapar no contexto correto e uma visão geral sobre Content Security Policy (CSP) como camada adicional de defesa.
Cross-Site Scripting (XSS) é uma das vulnerabilidades mais comuns e perigosas em aplicações web. Nesta aula, você aprenderá a identificar e prevenir ataques XSS em suas aplicações PHP, utilizando técnicas de escape adequadas e políticas de segurança de conteúdo. Vamos explorar desde o conceito básico até a implementação prática de defesas eficazes.
O XSS ocorre quando um atacante consegue injetar scripts maliciosos em páginas web visualizadas por outros usuários. Esses scripts podem roubar cookies, sessões, redirecionar usuários ou modificar o conteúdo da página. Para prevenir, é essencial tratar toda saída de dados que possa conter informações fornecidas pelo usuário, além de adotar medidas como CSP.
O que é
XSS (Cross-Site Scripting) é uma vulnerabilidade de segurança que permite a injeção de scripts do lado do cliente em páginas web. O atacante explora a falta de validação ou escape de entradas do usuário, fazendo com que o navegador execute código malicioso no contexto da aplicação. Existem três tipos principais: XSS Refletido, XSS Armazenado e XSS baseado em DOM.
No XSS Refletido, o payload malicioso é refletido imediatamente na resposta, geralmente via parâmetros de URL. No XSS Armazenado, o payload é persistido no servidor (como em um banco de dados) e exibido a todos os usuários que acessarem a página. Já o XSS baseado em DOM ocorre quando o JavaScript manipula o DOM de forma insegura, usando dados não confiáveis. Em PHP, o XSS geralmente acontece quando o desenvolvedor imprime dados sem escapar corretamente.
Exemplo de código vulnerável:
<?php
$nome = $_GET['nome'];
echo "Olá, $nome!";
?>Se um usuário acessar ?nome=<script>alert('XSS')</script>, o script será executado. Para prevenir, é necessário escapar a saída.
htmlspecialchars
A função htmlspecialchars() é a principal ferramenta em PHP para escapar saídas em HTML. Ela converte caracteres especiais como <, >, ", ' e & em suas entidades HTML, impedindo que sejam interpretados como código. A assinatura é:
htmlspecialchars(string $string, int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, string $encoding = null, bool $double_encode = true)O parâmetro $flags controla o comportamento: ENT_QUOTES converte aspas duplas e simples, ENT_NOQUOTES não converte aspas, ENT_SUBSTITUTE substitui caracteres inválidos por U+FFFD em vez de retornar string vazia. É recomendado usar ENT_QUOTES para cobrir todos os contextos de atributos.
Exemplo de uso:
$nome = $_GET['nome'];
echo "Olá, " . htmlspecialchars($nome, ENT_QUOTES, 'UTF-8') . "!";Com isso, a entrada maliciosa é exibida como texto puro, sem executar. É importante definir o encoding correto (geralmente UTF-8) e usar a função em toda saída que contenha dados não confiáveis.
Alternativas: htmlentities() converte todos os caracteres aplicáveis, mas é mais lento. strip_tags() remove tags, mas não é segura como única defesa. A melhor prática é usar htmlspecialchars() com flags adequadas.
Escape no contexto certo
Escapar dados não é uma solução única; o escape deve ser adequado ao contexto onde o dado será inserido. Por exemplo, em um atributo HTML, é necessário escapar aspas; em um contexto de URL, é necessário codificar a URL; em JavaScript, é necessário escapar strings para evitar injeção de código.
Contextos comuns:
- HTML body: usar
htmlspecialchars()comENT_QUOTES. - Atributos HTML: também usar
htmlspecialchars()comENT_QUOTES. - URLs: usar
urlencode()ourawurlencode()para parâmetros, e validar o esquema (http/https) para evitar javascript:. - JavaScript: usar
json_encode()para inserir dados em contexto JS, ou escapar comaddslashes()e codificação Unicode, mas o ideal é evitar interpolar dados diretamente. - CSS: escapar usando
addcslashes()ou codificação, mas é raro.
Exemplo de URL segura:
$url = "https://exemplo.com/pagina?nome=" . urlencode($nome);
echo "<a href='" . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . "'>Link</a>";Nunca confie em funções genéricas; entenda onde o dado será exibido e aplique o escape correspondente. Além disso, valide e filtre entradas no servidor, usando filter_var() ou expressões regulares.
CSP (visão geral)
Content Security Policy (CSP) é um cabeçalho HTTP que permite controlar quais recursos o navegador pode carregar, reduzindo o impacto de ataques XSS. Com CSP, você pode restringir a execução de scripts a fontes confiáveis, bloquear inline scripts e até mesmo reportar violações.
Para implementar CSP em PHP, envie o cabeçalho antes de qualquer saída:
header("Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.exemplo.com; style-src 'self' 'unsafe-inline'");Diretivas comuns: default-src define a política padrão, script-src controla fontes de scripts, style-src para estilos, img-src para imagens, e report-uri para enviar relatórios. Você pode usar 'unsafe-inline' para permitir inline, mas isso reduz a segurança.
É importante testar a política para não quebrar funcionalidades. Uma abordagem gradual é usar Content-Security-Policy-Report-Only para monitorar sem bloquear.
Exemplo de política restritiva:
header("Content-Security-Policy: default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self'; base-uri 'self'; form-action 'self'");CSP não elimina a necessidade de escapar saídas, mas adiciona uma camada extra de defesa, limitando o que o atacante pode executar mesmo se conseguir injetar algo.
Boas práticas e observações finais
- Sempre escape saídas com
htmlspecialchars()comENT_QUOTESe UTF-8. - Valide e filtre entradas no servidor, não apenas no cliente.
- Use templates com escape automático, como Twig ou Blade, para reduzir erros.
- Implemente CSP com uma política restritiva, mas testável.
- Mantenha o PHP e bibliotecas atualizadas.
- Considere usar um framework com proteções embutidas.
Referências
- MDN: Cross-site scripting
- PHP: htmlspecialchars
- OWASP: XSS
- MDN: Content Security Policy
- OWASP Top Ten
- PHP: urlencode
- PHP: filter_var
Exercícios
- Explique o que é XSS e cite os três tipos principais. Dê um exemplo de código PHP vulnerável a XSS refletido.✓ Resposta: XSS é uma vulnerabilidade que permite injeção de scripts maliciosos. Os três tipos são: Refletido, Armazenado e DOM-based. Exemplo vulnerável:
<?php echo $_GET['msg']; ?> - Qual a diferença entre
htmlspecialchars()ehtmlentities()? Em que situação você usaria cada um?✓ Resposta:htmlspecialchars()converte apenas os caracteres especiais principais (<, >, &, ", '), enquantohtmlentities()converte todos os caracteres que têm entidades HTML. Usehtmlspecialchars()para a maioria dos casos, pois é mais leve e suficiente. Usehtmlentities()se precisar converter caracteres acentuados em entidades para compatibilidade. - Escreva um código PHP que exiba um parâmetro GET 'nome' em um atributo HTML de forma segura, usando
htmlspecialchars().✓ Resposta:$nome = $_GET['nome'] ?? ''; echo "<input type='text' value='" . htmlspecialchars($nome, ENT_QUOTES, 'UTF-8') . "'>"; - O que é Content Security Policy (CSP) e como ela ajuda a prevenir XSS? Dê um exemplo de cabeçalho CSP em PHP.✓ Resposta: CSP é um cabeçalho HTTP que restringe as fontes de scripts e outros recursos, dificultando a execução de scripts maliciosos. Exemplo:
header("Content-Security-Policy: default-src 'self'; script-src 'self'"); - Considere o código:
echo "<a href='" . $_GET['url'] . "'>Link</a>";. Como você o tornaria seguro? Explique as etapas.✓ Resposta: Para tornar seguro, valide a URL para permitir apenas http/https, usefilter_var($url, FILTER_VALIDATE_URL)e depois escape comhtmlspecialchars(). Exemplo:$url = filter_var($_GET['url'] ?? '', FILTER_VALIDATE_URL); if ($url !== false) { echo "<a href='" . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . "'>Link</a>"; } else { echo "URL inválida"; }