Cross-Site Scripting (XSS): o Guia de Ataques de 2026

Web Security
15 min de leitura
Cross-Site Scripting (XSS): o Guia de Ataques de 2026
Nesta página
  1. O que é cross-site scripting (XSS)?
  2. Como funciona um ataque XSS: a causa raiz
  3. Os três tipos de XSS: refletido, armazenado e baseado no DOM
    1. XSS refletido
    2. XSS armazenado
    3. XSS baseado no DOM
  4. O que os atacantes fazem com o XSS
  5. Payloads XSS e exemplos
  6. Como prevenir o XSS
  7. Como testar a presença de XSS
  8. Considerações legais e éticas
  9. Perguntas frequentes
  10. Seus próximos passos

O cross-site scripting (XSS) acontece quando um site entrega ao seu navegador o JavaScript de outra pessoa e o navegador o executa como se o próprio site o tivesse escrito. Essa única confusão, código onde se esperava dados, basta para roubar uma sessão, reescrever uma página ou usar os privilégios de um usuário logado. A forma mais rápida de sentir como é fácil disparar isso é quebrar você mesmo uma entrada real, então abra o lab XSS Playground da HackerDNA e faça surgir uma caixa alert antes de ler a teoria.

O XSS está na família de injeção do OWASP Top 10 desde que a lista existe, e continua sendo um dos bugs web mais reportados em 2026. Este guia cobre os três tipos que você realmente vai encontrar, o que um ataque XSS permite a um atacante fazer, os payloads que provam isso e as defesas que o barram. Se você quer o ataque lado a lado com seu primo, nosso comparativo XSS vs CSRF é a leitura complementar.

Resumo: o cross-site scripting (XSS) é uma vulnerabilidade web em que uma aplicação retorna entrada do usuário sem escapar, de modo que o script de um atacante roda no navegador de outro usuário, sob a origem do site. Ele vem em três formas: refletido (payload devolvido em uma resposta), armazenado (payload salvo e servido a todos) e baseado no DOM (JavaScript do lado do cliente escreve dados não confiáveis na página). A correção central é a codificação de saída ciente do contexto, apoiada por uma Content Security Policy e pelo escape automático dos frameworks. Filtrar a entrada sozinho não o impede.

O que é cross-site scripting (XSS)?

O cross-site scripting é uma vulnerabilidade que permite a um atacante injetar scripts do lado do cliente em páginas que outras pessoas visualizam. O navegador não consegue distinguir o código injetado do código do próprio site, então executa o JavaScript do atacante com toda a confiança da origem do site: seus cookies, sua sessão, seu DOM.

O nome é um acidente histórico. O que importa não é que os scripts cruzem sites, é que a entrada do usuário cruza a fronteira de dados para código executável. Um campo de comentário, uma barra de busca, um parâmetro de URL, um nome de usuário, tudo o que uma página depois renderiza sem escapar pode carregar um payload que o navegador executa.

O XSS corresponde a CWE-79, e importa porque roda no contexto da vítima, não no do servidor. Um atacante que consegue um XSS em um app bancário não está no servidor, está dentro da sua aba logada, capaz de fazer tudo o que você pode fazer ali. É por isso que ele fica consistentemente entre as descobertas de maior volume nos programas de bug bounty.

Como funciona um ataque XSS: a causa raiz

Todo bug de XSS tem a mesma origem da injeção SQL: a aplicação mistura entrada não confiável em uma saída que ela depois interpreta. Para a SQLi, o interpretador é o banco de dados. Para o XSS, é o navegador, e a linguagem é HTML mais JavaScript.

Aqui está o erro na sua forma mais simples. Uma página devolve um termo de busca direto no HTML:

<!-- Vulnerável: a busca é escrita na página sem escapar -->
<p>Você buscou por: <?php echo $_GET['q']; ?></p>

Envie q=tenis e a página exibe "Você buscou por: tenis". Envie q=<script>alert(1)</script> e o navegador não imprime esses caracteres, ele os analisa como uma tag <script> de verdade e a executa. A entrada deixou de ser texto e virou parte da página. Nada na string dizia ao navegador que a tag do atacante deveria ser exibida, não executada.

É por isso que o bloqueio por lista negra falha aqui tanto quanto na SQLi. As equipes tentam remover <script> e os atacantes recorrem a <img src=x onerror=alert(1)>, ou a um svg, ou a uma URL JavaScript, ou a um manipulador de evento em uma tag que você esqueceu. As entradas perigosas são praticamente infinitas porque o problema não é o payload, é que a saída nunca foi codificada para o contexto em que caiu.

💻
Pratique agora: lab XSS Playground - injete um script em um campo sem escape e veja-o executar, para entender exatamente o que a codificação de saída tira de um atacante. No navegador, sem instalação.

Os três tipos de XSS: refletido, armazenado e baseado no DOM

Quais são os tipos de XSS? São três: o XSS refletido, em que o payload é devolvido na resposta imediata; o XSS armazenado, em que o payload é salvo no servidor e servido a cada visitante; e o XSS baseado no DOM, em que o JavaScript do lado do cliente escreve dados não confiáveis na página sem que o servidor jamais veja o payload. Eles diferem em onde a entrada reside e em quem é atingido.

XSS refletido

O XSS refletido pega uma entrada da requisição atual, geralmente um parâmetro de URL, e a escreve direto na resposta. O payload não é armazenado em lugar nenhum, então o ataque precisa de uma etapa de entrega: o atacante cria um link malicioso e convence uma vítima a clicar nele. Pense em uma página de busca que reflete sua consulta, ou em uma página de erro que devolve um valor inválido.

Um payload refletido viaja na URL:

https://shop.example/search?q=<script>alert(document.domain)</script>

Qualquer um que abra esse link executa o script na própria sessão em shop.example. O XSS refletido é a variante mais comum e a primeira em que você vai tropeçar ao testar, porque qualquer parâmetro devolvido em uma página é um candidato.

XSS armazenado

O XSS armazenado, também chamado de XSS persistente, é o perigoso. O payload é salvo na aplicação, um comentário, uma bio de perfil, um ticket de suporte, uma avaliação de produto, e depois servido a todos que visualizam aquele conteúdo. Não há link a enviar. A vítima simplesmente carrega uma página normal e o script do atacante já está lá, à espera.

Imagine um fórum em que o corpo de um post é renderizado sem codificação. Um atacante publica um comentário contendo um script, e cada usuário que abre o tópico o executa. Um XSS armazenado em uma página de alto tráfego chega perto de um incidente autopropagável, que é exatamente como o antigo worm Samy arrasou o MySpace em 2005, adicionando mais de um milhão de amigos em menos de um dia.

XSS baseado no DOM

O XSS baseado no DOM nunca toca a resposta do servidor. O código vulnerável é JavaScript rodando no navegador que lê de uma fonte em que não deveria confiar, como location.hash ou document.referrer, e a passa para um sink que executa markup, como innerHTML ou document.write.

// Vulnerável: o hash é escrito na página como HTML
document.getElementById('welcome').innerHTML =
    'Olá, ' + decodeURIComponent(location.hash.slice(1));

Carregue a página com #<img src=x onerror=alert(1)> e o navegador constrói aquele elemento de imagem e dispara o manipulador. Como o payload vive no fragmento após o #, muitas vezes ele nunca é enviado ao servidor, o que significa que filtros do lado do servidor e muitos WAFs nunca o veem. À medida que as aplicações de página única dominaram, o XSS no DOM passou de caso raro a descoberta de rotina.

O que os atacantes fazem com o XSS

Um alert(1) prova o bug, mas não é o objetivo. Assim que um atacante consegue executar JavaScript na sua sessão, ele herda o seu acesso à página. O impacto realista costuma cair em algumas categorias:

  • Sequestro de sessão. Ler document.cookie e enviá-lo para outro lugar entrega ao atacante a sua sessão logada, a menos que o cookie de sessão esteja marcado como HttpOnly. É essa única flag que torna o roubo de cookie bem menos trivial do que sugerem os tutoriais de uma década atrás.
  • Agir como o usuário. Mesmo sem o cookie, o script pode fazer requisições autenticadas em seu nome: mudar seu e-mail, adicionar um administrador, transferir fundos. Ele roda de dentro da sua sessão, então o app vê ações legítimas de um usuário legítimo.
  • Captura de credenciais. Um script pode reescrever a página para adicionar um falso pedido de login, ou um capturador de teclas em um real, colhendo o que você digita em um formulário que parece totalmente autêntico.
  • Desfiguração e phishing. Como o código controla o DOM, ele pode substituir conteúdo, injetar mensagens enganosas ou redirecionar para uma página controlada pelo atacante, tudo sob o domínio confiável exibido na barra de endereços.

Quando você testa aplicações reais, a descoberta que faz um relatório ser levado a sério raramente é a caixa de alerta. É o passo seguinte: provar que o payload pode realizar uma ação autenticada, em um lab ou em um escopo autorizado, para que a gravidade seja inegável. Essa é a diferença entre "valor refletido" e "tomada de conta" em um relatório de bug.

Payloads XSS e exemplos

Você não precisa de um payload exótico para achar XSS. Você precisa de uma sonda que sobreviva ao contexto em que cai. Comece injetando um marcador inofensivo e observe onde e como ele volta no HTML de origem, depois molde o payload ao contexto. Algumas sondas padrão de prova de conceito, do tipo usado todos os dias em testes autorizados:

  • A tag clássica: <script>alert(1)</script> funciona quando sua entrada cai entre tags em um corpo HTML.
  • A saída de atributo: "><svg onload=alert(1)> escapa de um valor colocado dentro de um atributo HTML.
  • O manipulador de evento: <img src=x onerror=alert(1)> roda quando <script> é filtrado mas outras tags não.
  • O contexto JavaScript: ';alert(1)// sai de uma string escrita dentro de um bloco de script inline.

A lição dessa lista é o contexto. A mesma entrada é inofensiva em um lugar e executável em outro, e é por isso que a correção também precisa ser ciente do contexto. Use alert(document.domain) em vez de alert(1) quando demonstrar uma descoberta, pois isso prova sob qual origem o script está rodando e se lê com clareza em um relatório.

Para uma referência real e curada de payloads a testar contra alvos que você está autorizado a testar, a lista PayloadsAllTheThings XSS mantida pela comunidade é a que a maioria dos testadores guarda nos favoritos. Trate-a como material de estudo, não como uma lista para disparar em massa.

Como prevenir o XSS

Como prevenir o cross-site scripting? Codifique a saída para o contexto em que ela aparece, para que os dados não confiáveis sejam sempre renderizados como texto inerte em vez de analisados como markup ou script. A codificação é a defesa principal, e uma Content Security Policy mais o escape automático de um framework moderno a transformam em defesa em profundidade. A folha de prevenção de XSS da OWASP detalha todo o conjunto de regras; aqui está o que realmente faz diferença.

  • Codificação de saída ciente do contexto. Escape os dados conforme onde eles caem. Codifique em HTML para corpos de elemento (< vira &lt;), codifique em atributo dentro de atributos, e nunca coloque dados não confiáveis diretamente em um bloco de script ou em um manipulador de evento. É a correção que encerra a vulnerabilidade.
  • Deixe o framework escapar por padrão. React, Angular e Vue codificam valores interpolados automaticamente, e é por isso que os apps modernos têm menos XSS que a era do PHP. A armadilha é a escapatória: dangerouslySetInnerHTML, o bypassSecurityTrust do Angular e qualquer atribuição bruta de innerHTML tiram você de volta da segurança. Audite cada uma.
  • Implante uma Content Security Policy. Uma CSP rígida que bloqueia scripts inline e restringe as fontes é uma segunda camada forte. Ela não corrige um bug, mas pode impedir que um payload execute ou chame para fora mesmo quando um passa despercebido.
  • Defina HttpOnly nos cookies de sessão. Isso impede que o JavaScript leia o cookie, o que neutraliza a forma mais direta de roubo de sessão. Combine-o com Secure e um valor SameSite sensato.
  • Sanitize apenas quando você realmente precisar renderizar HTML. Se os usuários enviam legitimamente texto rico, não faça seu próprio filtro na mão. Passe-o por uma biblioteca comprovada como o DOMPurify, construída e testada especificamente para remover markup perigoso mantendo a formatação segura.

Repare no que não está no topo dessa lista: a validação de entrada. Filtrar a entrada é uma camada de apoio útil, e rejeitar dados claramente malformados é boa higiene, mas não pode ser a sua defesa contra XSS. O mesmo valor é perigoso em um contexto de saída e inofensivo em outro, então o controle confiável vive na saída, onde você conhece o contexto. Valide na entrada, codifique na saída.

Como testar a presença de XSS

Como achar XSS em uma aplicação? Injete um marcador único em cada entrada, depois leia o HTML bruto da resposta para ver onde ele aparece e se foi codificado. Onde seu marcador voltar intacto e sem escape, molde um payload para aquele contexto exato e confirme que ele executa. Uma rotina prática:

  1. Mapeie cada entrada. Parâmetros de URL, campos de formulário, cabeçalhos, cookies, e tudo o que uma aplicação de página única lê do fragmento de URL. Cada um é uma fonte candidata.
  2. Injete primeiro um marcador, não um payload. Envie uma string inofensiva como xss7391 e procure-a na origem da resposta. Se ela aparecer codificada, aquele contexto está defendido. Se aparecer bruta, você tem uma pista.
  3. Case o payload com o contexto. Entre tags, dentro de um atributo ou em um bloco de script, cada um pede uma saída diferente. É aqui que entender os três contextos compensa.
  4. Automatize a amplitude. Um scanner no Burp Suite ou uma ferramenta como o Dalfox vai fuzzar centenas de parâmetros muito mais rápido que você na mão. Aponte-o apenas para sistemas que você possui ou está autorizado a testar.

Fazer isso na mão algumas vezes constrói o instinto de identificar uma saída sem escape em uma revisão de código, onde a prevenção é mais barata. Aprender a explorar o XSS e aprender a preveni-lo são a mesma habilidade vista dos dois lados, e rodar o ataque em um lab controlado é a forma mais rápida de tornar o hábito defensivo automático.

Considerações legais e éticas

Lembrete essencial: obtenha sempre autorização escrita explícita antes de testar qualquer aplicação para XSS. Disparar payloads contra um site que você não possui é acesso não autorizado sob o Computer Fraud and Abuse Act (EUA), o Computer Misuse Act (Reino Unido) e leis equivalentes no mundo todo, mesmo quando seu payload é uma inofensiva caixa alert.

  • Teste apenas em sistemas que você possui, em alvos de treino propositalmente vulneráveis, ou dentro do escopo definido de um bug bounty ou engajamento autorizado.
  • Use uma prova inofensiva como alert(document.domain) para demonstrar uma descoberta. Não implante payloads que roubem cookies ou dados de usuários reais para provar o impacto.
  • Se você encontrar XSS na aplicação de outra pessoa, reporte pelo processo de divulgação dela e pare por aí.
  • Os labs e cursos ligados aqui existem para que você pratique o ataque e a defesa legalmente, em alvos feitos exatamente para isso.

Perguntas frequentes

O que é cross-site scripting em termos simples?

O cross-site scripting (XSS) é uma vulnerabilidade web que permite a um atacante rodar o próprio JavaScript no navegador de outro usuário. Isso acontece quando um site exibe uma entrada do usuário sem codificá-la, de modo que o navegador trata a entrada como código em vez de texto. O script então roda com a confiança do site, dando ao atacante acesso à sessão e à página daquele usuário.

Quais são os três tipos de XSS?

O XSS refletido devolve o payload na resposta imediata, geralmente de um parâmetro de URL, então precisa que uma vítima clique em um link forjado. O XSS armazenado salva o payload no servidor e o serve a todos que visualizam o conteúdo. O XSS baseado no DOM acontece inteiramente em JavaScript do lado do cliente que escreve dados não confiáveis na página, muitas vezes sem que o servidor jamais veja o payload.

O XSS ainda é uma ameaça em 2026?

Sim. Os frameworks modernos escapam a saída automaticamente, o que reduziu o volume de XSS triviais, mas o bug continua sendo uma das descobertas mais comuns nos programas de bug bounty. Escapatórias como dangerouslySetInnerHTML, o innerHTML bruto em aplicações de página única e os sinks baseados no DOM mantêm o XSS bem vivo nos stacks atuais.

Como o XSS difere da injeção SQL?

Ambos são bugs de injeção, mas miram interpretadores diferentes. O XSS injeta código que roda no navegador da vítima, afetando a sessão daquele usuário. A injeção SQL injeta código que roda no banco de dados, afetando dados do lado do servidor. O XSS é um ataque do lado do cliente; a SQLi é do lado do servidor. A causa raiz em comum é misturar entrada não confiável em uma saída que é interpretada.

A validação de entrada impede o XSS?

Não sozinha. O bloqueio por lista negra de caracteres ou tags pode ser contornado com outras tags, manipuladores de evento, codificações e URLs JavaScript. A defesa confiável é a codificação de saída ciente do contexto, para que os dados não confiáveis sejam sempre renderizados como texto inerte. Use a validação de entrada como camada de apoio e adicione uma Content Security Policy para defesa em profundidade.

Seus próximos passos

O cross-site scripting se resume a uma única fronteira: no momento em que um site deixa uma entrada do usuário virar código no navegador de outra pessoa, o atacante está dentro daquela sessão. Os três tipos, refletido, armazenado e baseado no DOM, são apenas lugares diferentes em que a entrada pode cruzar a linha, e a defesa é a mesma ideia no sentido inverso: codifique a saída conforme o contexto, deixe seu framework escapar por padrão e adicione uma Content Security Policy para que um ponto esquecido não seja um presente de graça. A forma de fixar esse instinto é ver um payload executar uma vez, depois observar a codificação tirá-lo de cena. Comece com o plano gratuito da HackerDNA, sem cartão de crédito, e faça surgir seu primeiro script no lab XSS Playground. Quando quiser o XSS no contexto de toda a superfície de ataque web, o capítulo de XSS do nosso curso Web Attacks o percorre ao lado da injeção SQL, do CSRF e do resto do OWASP Top 10 em labs guiados no navegador. Aprenda a disparar o payload, depois escreva a página que o engole.

HackerDNA Team

Equipe HackerDNA

Escrito pela equipe HackerDNA - profissionais de cibersegurança que criam labs práticos de hacking e conteúdo educativo para ajudar você a desenvolver habilidades reais em segurança.

Conhecer a Equipe

Pronto para colocar isso em prática?

Pare de ler, comece a hackear. Ganhe experiência prática com mais de 170 labs de cibersegurança reais.

Comece a Hackear Grátis
16.000+ Hackers 100+ Labs & Cursos Grátis
Comece Grátis