
O que é XSS e como proteger aplicações web
Um campo de busca, um comentário, o nome de um usuário ou até mesmo um parâmetro presente na URL parecem apenas dados.
Até que a aplicação confunda esses dados com código.
É justamente nessa fronteira que nasce uma das vulnerabilidades mais conhecidas da web: o Cross-Site Scripting, ou simplesmente XSS.
Em um ataque XSS, conteúdo controlado por um invasor consegue chegar ao navegador de outra pessoa e ser interpretado como código pertencente à aplicação legítima.
O problema não está simplesmente em “aceitar caracteres perigosos”.
O problema está em permitir que dados não confiáveis atravessem a aplicação e sejam interpretados no contexto errado.
Apesar de ser uma vulnerabilidade conhecida há décadas, XSS continua extremamente relevante. No OWASP Top 10 de 2025, Cross-Site Scripting permanece dentro da categoria A05: Injection, que concentra diferentes falhas nas quais dados são interpretados como comandos ou código.
Neste guia, vamos entender como o XSS acontece, quais são seus principais tipos e, principalmente, como desenvolver aplicações web que não transformem dados do usuário em código executável.
O que é XSS?
XSS significa Cross-Site Scripting.
É uma vulnerabilidade de segurança em aplicações web que permite que conteúdo não confiável seja interpretado pelo navegador como HTML ou JavaScript pertencente à própria aplicação.
Em termos simples:
a aplicação esperava receber dados, mas o navegador acabou recebendo código.
Imagine uma funcionalidade de busca.
O usuário pesquisa:
E a aplicação exibe:
Até aqui, tudo certo.
O problema começa quando aquilo que veio do usuário é inserido diretamente no HTML sem o tratamento adequado.
Se o navegador conseguir interpretar parte desse valor como código, a aplicação abriu uma porta para XSS.
A PortSwigger define o XSS como uma vulnerabilidade capaz de comprometer as interações dos usuários com uma aplicação vulnerável, permitindo que código controlado pelo atacante seja executado no contexto daquela aplicação.
Por que o XSS é perigoso?
O JavaScript executado através de uma vulnerabilidade XSS não aparece para o navegador como um programa desconhecido vindo de outro site.
Ele está sendo executado dentro da origem da aplicação legítima.
Isso significa que, dependendo da aplicação e das proteções existentes, um XSS pode permitir:
executar ações em nome do usuário;
acessar informações disponíveis na página;
modificar elementos da interface;
capturar dados digitados;
realizar requisições autenticadas;
redirecionar usuários;
exibir formulários falsos;
comprometer contas;
atingir administradores com privilégios elevados.
A gravidade depende diretamente de onde a vulnerabilidade existe.
Um XSS em um site institucional público não possui necessariamente o mesmo impacto que um XSS dentro do painel administrativo de uma plataforma financeira.
Se o usuário comprometido possui grandes privilégios, o impacto também pode aumentar significativamente.
Como um ataque XSS acontece?
O fluxo básico costuma ser este:
Observe que o problema não é simplesmente receber dados de usuários.
Toda aplicação web faz isso.
O problema está no caminho que aquele dado percorre depois.
Uma entrada pode vir de:
Um dado armazenado no banco também não deve ser considerado automaticamente seguro.
Ele pode ter sido inserido anteriormente por outra pessoa, importado de outro sistema ou vindo de uma integração comprometida.
Uma boa regra é:
se a aplicação não controla completamente a origem de um dado, trate-o como não confiável.
Quais são os três principais tipos de XSS?
Os XSS normalmente são divididos em três grupos:
Tipo | Onde o dado malicioso aparece | Característica |
|---|---|---|
XSS refletido | Resposta atual da aplicação | Normalmente depende de uma requisição manipulada |
XSS armazenado | Banco de dados ou outro armazenamento | Pode atingir vários usuários posteriormente |
DOM XSS | Código executado no navegador | A falha ocorre no tratamento client-side |
PortSwigger e OWASP utilizam essa divisão para explicar as principais famílias da vulnerabilidade.
1. XSS refletido
No XSS refletido, um valor presente em uma requisição é devolvido imediatamente pela aplicação dentro da resposta sem o tratamento adequado.
Uma página de pesquisa poderia funcionar assim:
E renderizar:
O risco aparece quando o valor de q é colocado diretamente no HTML e o navegador pode interpretá-lo como marcação.
O fluxo seria:
Esse tipo de vulnerabilidade costuma aparecer em:
buscas;
mensagens de erro;
filtros;
páginas de resultado;
parâmetros de URL;
páginas que exibem novamente dados enviados anteriormente.
No XSS refletido, o conteúdo malicioso normalmente não permanece armazenado na aplicação.
2. XSS armazenado
O XSS armazenado, também chamado de persistent XSS, pode ser ainda mais perigoso.
Nesse caso, a aplicação salva o conteúdo não confiável e posteriormente o apresenta para outras pessoas.
Imagine um sistema que permita comentários.
Um usuário envia:
O servidor armazena o comentário e depois o exibe.
Agora imagine que a aplicação permita que HTML não confiável seja armazenado e renderizado sem sanitização adequada.
O fluxo se torna:
A diferença é importante.
O atacante não precisa necessariamente entregar uma URL manipulada para cada vítima.
O conteúdo já está dentro da própria aplicação.
Comentários, chats, perfis, descrições, sistemas de tickets, fóruns e áreas administrativas que exibem conteúdo inserido por usuários são pontos clássicos que merecem atenção.
3. DOM-based XSS
Existe ainda o DOM XSS.
Aqui, a vulnerabilidade pode acontecer inteiramente no navegador.
Considere este JavaScript:
O problema está em:
O valor vindo da URL está sendo entregue a uma API que interpreta HTML.
Se você quer inserir apenas texto, existe uma alternativa muito mais segura:
textContent trata o conteúdo como texto em vez de pedir ao navegador que faça o parsing daquele valor como HTML. A própria MDN recomenda evitar innerHTML quando o objetivo é inserir somente texto, justamente pelo risco de XSS.
Esse pequeno detalhe representa uma ideia fundamental da segurança web:
use uma API que corresponda exatamente àquilo que você precisa fazer.
O problema não é apenas <script>
Quando desenvolvedores começam a estudar XSS, é comum associar a vulnerabilidade apenas à tag:
Isso é perigoso porque cria uma falsa sensação de segurança.
Existem várias maneiras pelas quais HTML e APIs do navegador podem criar contextos executáveis.
É por isso que tentar resolver XSS simplesmente com:
não é uma defesa real.
A própria OWASP mantém material extenso mostrando por que filtros baseados em padrões específicos podem ser contornados.
A abordagem correta não é tentar adivinhar todas as coisas que um atacante poderia escrever.
É garantir que o navegador nunca interprete aquele dado em um contexto executável indevido.
Validação, escape e sanitização não são a mesma coisa
Esse é um dos conceitos mais importantes do artigo.
Validação
Validação pergunta:
esse dado corresponde ao formato esperado?
Exemplo:
ou:
Uma lista explícita de valores válidos costuma ser melhor do que tentar manter uma lista infinita de valores proibidos.
A validação é importante, mas não substitui o tratamento de saída.
Escape ou output encoding
Escape pergunta:
como esse valor deve ser representado com segurança neste contexto?
Por exemplo, se o dado será exibido como texto dentro de HTML, caracteres especiais precisam ser representados de forma que o navegador não os interprete como marcação.
Algo como:
será exibido como texto:
em vez de criar necessariamente um elemento HTML.
O ponto importante é que o escape depende do contexto.
HTML, atributos HTML, JavaScript, CSS e URLs possuem regras diferentes.
Não existe uma função universal de escape que possa ser aplicada cegamente em qualquer lugar.
A OWASP enfatiza exatamente essa codificação de saída específica para cada contexto.
Sanitização
Sanitização é utilizada quando você realmente precisa permitir algum HTML.
Imagine um editor de texto rico em que o usuário pode escrever:
Nesse caso, você não quer transformar todo HTML em texto.
Você quer permitir alguns elementos e remover construções perigosas.
É aí que entra uma biblioteca de sanitização.
Um exemplo bastante utilizado no ecossistema JavaScript é o DOMPurify.
Depois:
Implementar seu próprio sanitizador HTML costuma ser uma péssima ideia.
Browsers têm parsers complexos e existem inúmeras variações possíveis de marcação.
A regra de ouro: codifique a saída de acordo com o contexto
Considere:
Esse é um contexto HTML.
Agora:
Esse é um contexto de atributo.
Agora:
Esse é um contexto JavaScript.
E:
introduz ainda questões relacionadas ao contexto de URL.
As defesas necessárias não são automaticamente iguais.
Esse é um dos motivos pelos quais frameworks modernos são tão úteis.
Frameworks modernos já protegem contra XSS?
Em muitos casos, sim.
Mas não completamente.
Frameworks modernos normalmente escapam automaticamente valores inseridos pela forma padrão de templating.
Em Vue, por exemplo:
é muito diferente de executar HTML fornecido pelo usuário.
Em React:
também utiliza o mecanismo de renderização do framework em vez de concatenar HTML manualmente.
Isso reduz bastante a superfície de XSS.
A própria OWASP reconhece que frameworks modernos ajudam através de auto-escaping e sistemas de templating seguros.
O perigo aparece quando o desenvolvedor decide escapar dessa proteção.
Cuidado com v-html
No Vue, existe:
Isso possui usos legítimos.
Por exemplo, exibir HTML vindo de um CMS previamente sanitizado.
Mas se content vier diretamente de um usuário:
você precisa considerar aquilo como uma superfície sensível.
Quando HTML realmente precisa ser renderizado, sanitize o conteúdo com uma biblioteca apropriada antes de entregá-lo ao componente.
Cuidado com dangerouslySetInnerHTML
React deixa a intenção bastante explícita no próprio nome:
A existência dessa API não significa que ela seja proibida.
Significa que o desenvolvedor assumiu a responsabilidade de garantir que aquele HTML é seguro.
A OWASP cita explicitamente o uso de dangerouslySetInnerHTML com HTML não sanitizado como uma maneira de contornar as proteções fornecidas por frameworks.
Cuidado com innerHTML
Em JavaScript puro:
deveria imediatamente acender uma pequena luz vermelha durante um code review.
Nem todo uso de innerHTML é vulnerável.
O problema surge quando o valor pode ser influenciado por uma fonte não confiável.
Se você precisa inserir texto:
normalmente é a escolha correta.
Content Security Policy: uma segunda muralha
Mesmo com uma aplicação bem desenvolvida, segurança funciona melhor em camadas.
Uma delas é a Content Security Policy, ou CSP.
CSP é uma política enviada pelo servidor que determina quais origens podem fornecer determinados recursos e quais scripts podem ser executados.
Uma política simplificada poderia começar assim:
Isso não deve ser simplesmente copiado para qualquer projeto.
Uma boa CSP precisa considerar os recursos utilizados pela aplicação.
Google Analytics, fontes, CDN, mapas, pixels, scripts externos e integrações podem exigir ajustes.
Também existem estratégias usando nonces ou hashes para permitir scripts específicos.
A ideia é:
A PortSwigger descreve CSP como uma camada adicional capaz de reduzir a capacidade de exploração caso uma vulnerabilidade XSS ainda exista.
Mas existe uma distinção fundamental:
CSP não deve ser utilizada como desculpa para manter XSS no código.
Ela é uma defesa adicional.
Não a defesa primária.
Trusted Types: uma proteção moderna contra DOM XSS
Existe uma proteção particularmente interessante nas aplicações modernas: Trusted Types.
APIs como:
são chamadas de injection sinks porque recebem valores que podem acabar interpretados como HTML.
Trusted Types permite restringir esses pontos para que não aceitem strings comuns indiscriminadamente.
Com CSP:
determinados sinks passam a exigir valores produzidos por políticas de Trusted Types.
Um fluxo pode ficar assim:
Em vez de:
Em 2026 essa abordagem ganhou ainda mais relevância: a MDN passou a classificar require-trusted-types-for como Baseline 2026 nos navegadores modernos, embora aplicações ainda precisem considerar compatibilidade com clientes mais antigos.
Esse é um diferencial importante para aplicações web de grande porte com muito código client-side.
Cookies HttpOnly ajudam contra XSS?
Sim, mas com uma ressalva importante.
Um cookie de sessão pode ser configurado com:
HttpOnly impede que JavaScript comum acesse aquele cookie através de:
Isso reduz um dos possíveis impactos de XSS envolvendo roubo direto do cookie de sessão.
Mas isso não corrige a vulnerabilidade XSS.
Mesmo sem conseguir ler o cookie, um script executado dentro da aplicação ainda pode tentar realizar ações usando a sessão do usuário.
Portanto:
É uma proteção complementar.
WAF resolve XSS?
Não sozinho.
Um Web Application Firewall pode detectar e bloquear determinados padrões.
Isso é útil como camada adicional.
Mas a OWASP alerta que WAFs não resolvem a causa raiz do problema e podem falhar diante de variações de payload ou vulnerabilidades inteiramente baseadas no DOM.
Pense no WAF como o segurança na porta.
Ele ajuda.
Mas não substitui colocar fechaduras nas portas internas da aplicação.
Segurança em aplicações Nuxt
Em aplicações Nuxt existem alguns recursos interessantes.
Quando valores controlados pelo usuário precisam aparecer no <head>, por exemplo, o Nuxt fornece:
A documentação recomenda essa versão para dados provenientes de usuários, restringindo atributos potencialmente perigosos.
Também existem ferramentas do ecossistema como nuxt-security, que ajudam a configurar headers, CSP e outras proteções alinhadas a práticas do OWASP.
Isso não significa:
Segurança continua dependendo da arquitetura inteira.
Mas utilizar os mecanismos nativos do framework reduz espaço para erros desnecessários.
Como proteger uma aplicação contra XSS
Uma estratégia madura não possui uma única defesa.
Ela possui camadas:
É a soma dessas medidas que cria uma aplicação realmente resistente.
1. Utilize a renderização padrão do framework
Prefira:
em vez de:
quando você quer apenas mostrar texto.
Prefira:
em vez de:
quando não existe necessidade de HTML.
Não contorne mecanismos seguros do framework sem um motivo real.
2. Evite construir HTML concatenando strings
Evite:
Quando possível, trabalhe com o DOM:
Agora o navegador sabe que userName é texto.
Não uma nova parte do documento HTML.
3. Sanitize HTML permitido
Se um sistema possui:
editor de artigos;
descrição rica;
comentários formatados;
templates;
campos WYSIWYG;
e precisa aceitar HTML, utilize uma biblioteca especializada.
Não tente manter manualmente uma coleção de replace() para remover códigos perigosos.
4. Valide URLs
Outro caso esquecido são URLs inseridas por usuários.
Por exemplo:
A aplicação precisa controlar quais protocolos são permitidos.
Normalmente:
quando realmente necessários.
A PortSwigger recomenda abordagem baseada em allowlist para protocolos em vez de tentar listar todas as alternativas potencialmente perigosas.
5. Configure uma CSP forte
Comece em modo de relatório quando necessário.
Mapeie dependências.
Remova scripts inline desnecessários.
Evite:
sempre que a arquitetura permitir uma estratégia mais segura.
Use nonces ou hashes quando fizer sentido.
E revise a política sempre que novas integrações forem adicionadas.
6. Proteja cookies de sessão
Utilize atributos adequados:
Sessões também devem possuir expiração adequada e escopo de domínio restrito.
7. Não confie em dados vindos do banco
Isso é importante.
Um dado armazenado pode ter sido:
Banco de dados não é sinônimo de fonte confiável.
A defesa precisa existir no momento correto de utilização do dado.
Como testar sua aplicação contra XSS
Existem três níveis importantes.
Code review
Procure APIs sensíveis como:
A presença delas não significa automaticamente vulnerabilidade.
Ela significa:
este ponto merece revisão.
Análise automatizada
Ferramentas SAST e DAST podem ajudar a detectar fluxos inseguros.
Scanners como Burp Suite também possuem mecanismos específicos para identificação de XSS.
Testes manuais
Fluxos complexos exigem análise humana.
Principalmente:
conteúdo armazenado;
editores HTML;
componentes dinâmicos;
dados carregados por APIs;
painéis administrativos;
funcionalidades executadas somente após autenticação.
Nunca realize testes ofensivos em aplicações de terceiros sem autorização.
Checklist de prevenção contra XSS
Antes de colocar uma aplicação em produção, revise:
Verificação | Status |
|---|---|
Templates utilizam escape automático | ☐ |
Não existe HTML do usuário inserido diretamente | ☐ |
| ☐ |
| ☐ |
HTML permitido passa por sanitização | ☐ |
URLs possuem protocolos permitidos | ☐ |
Saídas utilizam encoding correto para cada contexto | ☐ |
Existe Content Security Policy | ☐ |
Cookies sensíveis utilizam HttpOnly e Secure | ☐ |
Dependências estão atualizadas | ☐ |
Inputs possuem validação server-side | ☐ |
Dados de APIs externas são tratados como não confiáveis | ☐ |
Aplicação passou por análise automatizada | ☐ |
Fluxos sensíveis passaram por code review | ☐ |
Logs e monitoramento estão configurados | ☐ |
Erros comuns na prevenção de XSS
“Eu valido tudo no frontend”
Frontend pode ser modificado pelo usuário.
Validações importantes precisam existir também no ambiente confiável da aplicação.
“Bloqueei a palavra script”
Isso não resolve XSS.
O navegador possui muitos contextos capazes de interpretar conteúdo ativo.
“Uso React/Vue, então não tenho XSS”
Frameworks reduzem o risco.
Eles não impedem um desenvolvedor de utilizar APIs inseguras.
“Instalei um WAF”
Excelente camada adicional.
Mas não substitui corrigir o código.
“Tenho CSP”
Ótimo.
Mas CSP também é uma camada de mitigação.
A vulnerabilidade ainda deve ser eliminada.
“Tudo que vem do banco é seguro”
Nem sempre.
O banco pode simplesmente estar armazenando o conteúdo malicioso para você.
XSS e SQL Injection são a mesma coisa?
Não.
Ambos pertencem à família de ataques de injeção, mas afetam contextos diferentes.
No SQL Injection:
No XSS:
Essa comparação ajuda a enxergar a causa conceitual.
Em ambos os casos existe uma quebra da fronteira entre:
e:
XSS e CSRF são a mesma coisa?
Também não.
Em um XSS, código controlado pelo atacante é executado dentro do contexto da aplicação.
Em CSRF, o objetivo normalmente é induzir um navegador autenticado a realizar uma ação não desejada.
As duas vulnerabilidades podem até interagir em determinados cenários, mas possuem causas e defesas diferentes.
O HTTPS protege contra XSS?
Não.
HTTPS protege a comunicação entre navegador e servidor.
Ele evita que terceiros alterem ou leiam facilmente o tráfego durante o transporte.
Mas se a própria aplicação envia ao navegador conteúdo inseguro, HTTPS entregará esse conteúdo inseguro perfeitamente criptografado.
Portanto:
continua sendo:
Perguntas frequentes sobre XSS
O que significa XSS?
XSS significa Cross-Site Scripting. É uma vulnerabilidade em que dados não confiáveis conseguem ser interpretados como conteúdo executável dentro de uma aplicação web.
Quais são os principais tipos de XSS?
Os três principais são XSS refletido, XSS armazenado e DOM-based XSS.
XSS acontece apenas com JavaScript?
JavaScript é o principal mecanismo associado à exploração, mas a superfície envolve HTML, atributos, URLs, CSS e diferentes APIs do navegador. Por isso, a defesa deve considerar o contexto em que o conteúdo é inserido.
Sanitizar input evita XSS?
Sanitização pode ser necessária quando HTML é permitido, mas não deve ser confundida com output encoding. A proteção depende do contexto e normalmente envolve várias técnicas trabalhando em conjunto.
Content Security Policy impede todo XSS?
Não. CSP reduz a superfície e pode dificultar a exploração, mas deve ser considerada uma camada adicional de defesa.
Frameworks como Vue e React protegem contra XSS?
Eles possuem mecanismos de escape automático que ajudam bastante. Porém, APIs como v-html, dangerouslySetInnerHTML e manipulação direta do DOM podem reintroduzir o risco.
Um WAF resolve XSS?
Não sozinho. WAF pode bloquear determinados ataques, mas não elimina a vulnerabilidade presente no código.
HttpOnly impede XSS?
Não. Ele restringe o acesso JavaScript ao cookie, reduzindo alguns impactos, mas o script malicioso ainda pode executar dentro da aplicação.
Segurança precisa nascer junto com o sistema
XSS é um ótimo exemplo de por que segurança não deveria ser uma camada adicionada apenas depois que o sistema está pronto.
Uma aplicação pode ter:
ótima interface;
servidores rápidos;
autenticação;
banco de dados;
dashboards;
automações;
e ainda assim possuir uma vulnerabilidade criada por uma única linha aparentemente inofensiva:
Desenvolver software seguro significa entender quais dados entram, por onde eles passam e em qual contexto serão utilizados.
Não existe um botão mágico chamado “proteger contra XSS”.
Existe arquitetura.
Existem boas escolhas de implementação.
Existem revisões.
Existem testes.
E existem várias camadas trabalhando juntas.
Está desenvolvendo um sistema para sua empresa?
Na Menzzo, desenvolvemos sistemas personalizados pensando não apenas nas funcionalidades que aparecem na tela, mas também na arquitetura, permissões, validações, integrações e segurança necessárias para sustentar a operação.
Se sua empresa ainda depende de vários softwares desconectados ou precisa construir uma plataforma própria com regras específicas, podemos desenvolver uma solução em torno do seu processo.
Fale com a Menzzo e conheça nossas soluções em desenvolvimento de sistemas personalizados.






