O que é XSS (cross-site scripting)? 3 tipos e como evitar

Imagine um campo de comentários num blog. Alguém, em vez de escrever "ótimo post", escreve um trecho de JavaScript. Se o site guarda esse texto e o entrega a cada visitante sem tratar, o navegador de todo mundo que abrir a página vai executar o código, achando que ele faz parte do site. Isso é XSS (cross-site scripting), uma das falhas web mais antigas e mais repetidas — e a que sustenta boa parte do roubo de cookie de sessão.
O que é XSS, afinal?
O que é cross-site scripting (XSS)?
XSS é uma vulnerabilidade em que um site inclui, na página, texto controlado pelo atacante sem tratá-lo, e o navegador da vítima executa esse texto como JavaScript. O código roda com as mesmas permissões do site legítimo.
Segundo a documentação da MDN, como o código roda na origem do site, ele pode acessar o conteúdo da página, o armazenamento local e as credenciais, e fazer requisições em nome do usuário. O ataque exige duas condições: o site aceita uma entrada que o atacante consegue montar, e a coloca na página sem tratamento.
Repare no nome do problema: não é o servidor que é invadido. O alvo é o navegador do visitante, que confia no site e, por tabela, no que o site entrega.
Os 3 tipos de XSS
XSS refletido
O servidor pega um parâmetro da URL e devolve na resposta sem codificar. Exemplo clássico: uma página de busca que mostra "Você buscou: termo". O atacante monta um link com o código no lugar do termo e convence a vítima a clicar — por e-mail, mensagem ou anúncio. É o mesmo roteiro do phishing por e-mail, só que o link aponta para um site legítimo (e vulnerável).
XSS armazenado (persistente)
O código fica salvo no banco de dados — num comentário, perfil, mensagem de suporte — e é servido a todo mundo que abrir a página. A MDN o descreve como particularmente grave, porque não depende de a vítima clicar num link malicioso: basta visitar a página. Painéis administrativos que exibem mensagens de usuários são alvo frequente, já que o "visitante" ali é alguém com muito privilégio.
XSS baseado em DOM
Aqui o servidor nem participa: o próprio JavaScript da página pega um dado (da URL, por exemplo) e o joga no DOM de forma insegura. O padrão típico é usar innerHTML com texto do usuário:
// vulnerável: o que vier em "usuario" vira HTML
bemvindo.innerHTML = `Olá, ${usuario}!`;
// seguro: o que vier em “usuario” vira texto puro
bemvindo.textContent = Olá, ${usuario}!;
Se usuario contiver uma tag <img> com um manipulador de evento, a primeira versão executa o código. A segunda mostra o texto literalmente na tela.
O que um atacante consegue fazer com XSS?
- Roubar a sessão: ler cookies sem a flag
HttpOnlye entrar na conta da vítima sem saber a senha. - Agir como o usuário: mesmo com o cookie protegido, o script pode enviar requisições autenticadas — trocar e-mail da conta, fazer uma transferência, publicar conteúdo.
- Exibir formulários falsos dentro do site verdadeiro, para capturar senha ou dados de cartão.
- Redirecionar a vítima para páginas de golpe ou de download de malware.
É também por isso que 2FA e senha forte não resolvem sozinhos: o código malicioso age depois do login, dentro da sessão já autenticada.
Como evitar XSS (para quem desenvolve ou mantém um site)
A defesa principal é tratar todo dado não confiável no ponto onde ele é escrito na página. O Cheat Sheet de prevenção de XSS da OWASP resume bem: o que importa é a cobertura — um app que protege 99 lugares e esquece um continua vulnerável.
- Codificar a saída conforme o contexto. Texto no HTML, valor de atributo, parâmetro de URL e string dentro de script pedem codificações diferentes. Os caracteres
<,>,",'e&viram entidades HTML no corpo da página. - Usar o escape automático do framework e desconfiar das saídas de emergência:
dangerouslySetInnerHTMLno React,bypassSecurityTrust*no Angular,unsafeHTMLno Lit. O nome já avisa. - Preferir
textContentainnerHTML. Se o HTML do usuário for mesmo necessário (editor de texto rico), passe por um sanitizador mantido, como o DOMPurify, que a OWASP recomenda, e não mexa no conteúdo depois. - Colocar aspas nos atributos. Atributo sem aspas deixa o atacante "fechar" o valor e adicionar um evento próprio.
- Nunca inserir entrada do usuário dentro de
<script>ou<style>— a MDN considera quase sempre inseguro.
CSP e HttpOnly resolvem XSS?
Não. Content Security Policy (CSP) e o cookie HttpOnly reduzem o estrago, mas não corrigem a falha. A OWASP diz explicitamente que a CSP não deve ser a defesa principal.
Uma CSP estrita, com nonce ou hash, faz o navegador recusar scripts injetados e bloqueia manipuladores de evento inline e eval(). A flag HttpOnly impede que o JavaScript leia o cookie de sessão. Os dois valem como camada extra, mas um script injetado ainda consegue agir dentro da sessão. Por fim, o Trusted Types (require-trusted-types-for 'script') obriga os pontos perigosos do DOM a receber valores passados por um sanitizador — não sanitiza sozinho, só garante que a sua função foi chamada, e o suporte entre navegadores ainda é desigual.
E se eu sou só usuário?
Você não corrige o código do site, mas reduz o risco:
- Desconfie de links de busca ou de login com URL muito longa e cheia de símbolos, vindos de mensagem inesperada.
- Deslogue de serviços sensíveis (banco, painel administrativo) quando terminar, para encurtar a janela de uma sessão roubada.
- Mantenha o navegador atualizado e use poucas extensões — uma extensão maliciosa também executa código dentro das suas páginas.
- Se um site mostrar comportamento estranho (janela de login do nada, redirecionamento), feche a aba e avise o responsável.
A lição maior
XSS e SQL Injection compartilham a mesma raiz: dado e comando misturados no mesmo texto. A OWASP, aliás, agrupa o XSS na categoria Injection no Top 10 de 2021. Mudou o alvo (o banco no SQLi, o navegador no XSS), não a causa. Se você quer ver a mesma ideia aplicada a chatbots, leia Prompt Injection: o que é e por que ameaça chatbots de IA.
Fontes consultadas: MDN Web Docs (Cross-site scripting), OWASP Cross Site Scripting Prevention Cheat Sheet.




Comentários