DOM-based XSS é uma vulnerabilidade de cross-site scripting que vive inteiramente no código client-side. O próprio JavaScript da página lê entradas controláveis pelo atacante, na maioria das vezes a partir da URL, e as escreve no DOM através de um sink inseguro como innerHTML, executando o script do atacante. Diferentemente do XSS refletido ou armazenado, o payload malicioso pode nunca aparecer em nenhuma resposta do servidor.
Resumo: a classe de XSS que os servidores nunca veem
- O que é: Uma falha de cross-site scripting que vive inteiramente no JavaScript do lado do cliente, onde o próprio código da página escreve entrada controlada pelo atacante no DOM de forma insegura.
- Por que se esconde: O payload costuma viajar no fragmento da URL, que nunca é enviado ao servidor, então WAFs e logs de servidor podem deixá-lo passar por completo.
- Detecção: Auditar os fluxos de dados da origem ao sink e observar sessões reais na camada do navegador, o único lugar onde um ataque apenas no DOM é visível.
Sem tempo? Veja o monitoramento do lado do cliente da cside. Ele observa sessões reais na camada do navegador, onde um ataque apenas no DOM se torna visível.
Como funciona um ataque de DOM XSS?
A vulnerabilidade é um fluxo de dados de um source que o atacante controla para um sink que interpreta texto como código ou marcação:
// vulnerable: fragment flows straight into a sink
const name = decodeURIComponent(location.hash.slice(1));
document.querySelector("#welcome").innerHTML = "Hello " + name;
// https://example.com/#<img src=x onerror=stealCookies()>
Sources comuns: location.hash, location.search, document.referrer, window.name, dados de postMessage e armazenamento client-side. Sinks comuns: innerHTML/outerHTML, document.write, eval e Function, o .html() do jQuery, e escritas de atributos que criam manipuladores ou URLs (onclick, href com javascript:).
Como o fragmento da URL nunca é enviado ao servidor, um payload após o # pode explorar a página enquanto o servidor vê uma requisição perfeitamente normal.
XSS refletido vs armazenado vs DOM-based
| XSS refletido | XSS armazenado | DOM-based XSS | |
|---|---|---|---|
| Onde o payload vive | Na requisição, ecoado pelo servidor | No banco de dados do servidor | Na entrada client-side (muitas vezes o fragmento da URL) |
| A resposta do servidor contém o payload? | ✓ | ✓ | Muitas vezes ✗ |
| Visível para WAFs / logs do servidor | Geralmente | Geralmente | Muitas vezes não |
| Onde é corrigido | Codificação de saída no servidor | Sanitização + codificação no servidor | Código client-side: sinks seguros, sanitização |
Por que o DOM XSS é fácil de passar despercebido
As defesas do lado do servidor inspecionam requisições e respostas; o DOM XSS pode contornar ambas. Os frameworks reduzem o risco (React e afins fazem escape por padrão), mas dangerouslySetInnerHTML, o mau uso de templates e os scripts de terceiros na página o reintroduzem. E uma página moderna executa dezenas de scripts que você não escreveu: um sink inseguro em qualquer um deles é um sink inseguro na sua página. Esse problema de composição é o cerne da segurança client-side.
Como prevenir e detectar
- Prefira sinks seguros:
textContentem vez deinnerHTML; evite completamenteevale manipuladores construídos com strings. - Sanitize quando o HTML for inevitável (DOMPurify ou a emergente Sanitizer API), e adote Trusted Types onde houver suporte: eles transformam escritas em sinks inseguros em violações de política.
- Implante uma Content Security Policy rigorosa com nonces; ela bloqueia muitos estilos de payload, embora não toda a classe de ataque.
- Observe o comportamento em tempo de execução: a exploração acaba se manifestando como scripts fazendo coisas que não deveriam: novas requisições de saída, escritas no DOM em formulários de pagamento, listeners inesperados. O monitoramento client-side da cside observa sessões reais na camada do navegador, que é o único lugar onde um ataque restrito ao DOM é sequer visível.









