Prevenir a injeção de JavaScript não é uma única tarefa. É fechar várias portas distintas que levam todas à mesma sala: código controlado por um atacante rodando nos navegadores dos seus visitantes com todos os privilégios da sua própria página. Algumas dessas portas são bugs no seu código. A maioria são scripts que você carregou deliberadamente e que depois se tornaram hostis. Este guia percorre os controles que realmente reduzem o risco, na ordem que oferece mais proteção com o menor esforço, e é honesto sobre onde cada controle para.
Se você quer primeiro o contexto, o que é a injeção de JavaScript cobre o mecanismo e todos os caminhos pelos quais o código estranho entra numa página. Este artigo é a continuação prática: o que fazer a respeito.
Como prevenir a injeção de JavaScript, em resumo
- Feche a classe XSS no seu próprio código. Codifique na saída, use sinks do DOM seguros, sanitize o HTML não confiável e ative Trusted Types.
- Restrinja de onde o script pode vir. Uma Content Security Policy rigorosa baseada em nonce bloqueia as origens de script não autorizadas.
- Fixe o que puder. Subresource Integrity congela as dependências estáticas de terceiros num hash conhecido.
- Reduza a árvore de dependências. Cada script de terceiros que você remove é um caminho de injeção a menos.
- Monitore o tempo de execução. Vigie cada script que roda em sessões reais do navegador, porque a prevenção não consegue alcançar o comprometimento de um fornecedor de confiança.
Os caminhos de injeção contra os quais você se defende
A prevenção só faz sentido quando você sabe o que está prevenindo. O JavaScript é injetado por quatro caminhos amplos, e cada um pede um controle diferente:
| Caminho de injeção | Como acontece | O controle que ajuda |
|---|---|---|
| Cross-site scripting (XSS) | Uma falha no seu código permite que a entrada do atacante rode como script | Codificação de saída, sinks seguros, sanitização, Trusted Types |
| Script de terceiros comprometido | Um fornecedor cuja tag você carrega distribui código malicioso | Revisão de dependências, CSP, Subresource Integrity, monitoramento em execução |
| Magecart / skimmers | Atacantes plantam código de roubo de cartões em páginas de pagamento, muitas vezes via uma dependência comprometida | Monitoramento da carga em execução no checkout, controles PCI DSS |
| Extensões maliciosas / malware do cliente | O código é injetado na própria máquina do visitante, em cada site que ele visita | Detecção em execução; você não pode corrigir o navegador do visitante |
O primeiro caminho é uma falha de código que é sua e você pode corrigir. O resto são falhas de confiança: o código foi convidado, ou vive numa máquina que você não controla. Essa distinção importa, porque explica por que corrigir o seu próprio código é necessário mas nunca suficiente.
Passo 1: Feche a classe XSS no seu próprio código
O cross-site scripting é o único caminho de injeção inteiramente sob o seu controle, então comece por aqui. O XSS acontece quando um dado fornecido por um usuário é escrito na página num lugar onde o navegador o tratará como código em vez de texto. As correções são bem compreendidas:
- Codifique na saída, não só na entrada. A mesma string é segura num contexto e perigosa noutro, então codifique os dados para o contexto exato onde eles chegam: corpo HTML, atributo HTML, JavaScript, URL ou CSS. A codificação de saída consciente do contexto é o controle anti-XSS mais eficaz.
- Use sinks do DOM seguros. Atribua dados não confiáveis com
textContentem vez deinnerHTML, e eviteeval,document.writeesetAttributeem manipuladores de eventos. Esses "sinks" são onde as strings injetadas se tornam executáveis. - Sanitize o HTML que você precisa renderizar. Quando você realmente precisar renderizar HTML fornecido pelo usuário (um comentário em texto rico, por exemplo), passe-o por um sanitizador mantido que remove o script e os manipuladores de eventos, em vez de escrever o seu próprio filtro.
- Ative Trusted Types. Trusted Types fazem os sinks perigosos do DOM recusarem strings simples no nível do navegador, de modo que uma carga injetada não consegue alcançá-los mesmo que um bug escape à revisão de código. São aplicados por uma diretiva de CSP, o que leva diretamente ao próximo passo.
Corrigir a classe XSS remove a porta que um atacante abre explorando o seu código. Não faz nada quanto às portas que você mesmo abriu ao carregar o script de outra pessoa, que é de onde vem a maioria das violações modernas do lado do cliente.
Passo 2: Restrinja as origens com uma Content Security Policy
Uma Content Security Policy diz ao navegador quais origens estão autorizadas a carregar e executar script na sua página. Uma CSP rigorosa baseada em nonce é um dos controles individuais mais fortes que você pode implantar contra a injeção: o script que não vem de uma origem permitida, e que não carrega o nonce correto por requisição, simplesmente não roda. Os blocos <script> embutidos que um atacante injeta por meio de um furo XSS são bloqueados por padrão.
Duas observações práticas. Primeiro, prefira nonces a listas de hosts permitidos sempre que puder; uma lista script-src ampla reabre em silêncio caminhos que você pretendia fechar. Segundo, e este é o limite importante: a CSP autoriza origens, não julga o comportamento. Assim que você autoriza o domínio de um fornecedor para que o script legítimo dele rode, a CSP não tem como saber se esse script está se comportando ou roubando dados do seu formulário. Se o fornecedor for comprometido na origem, o domínio dele continua na sua lista de permissões e a versão maliciosa carrega sem problemas. A CSP é necessária. Não é a resposta completa.
Passo 3: Fixe as dependências estáticas com Subresource Integrity
Subresource Integrity (SRI) permite anexar um hash criptográfico a uma tag <script> ou <link>. O navegador calcula o hash do arquivo que baixou e se recusa a executá-lo se o hash não corresponder, o que significa que um arquivo estático de terceiros não pode ser trocado por uma versão adulterada sem que a mudança seja bloqueada.
O SRI é excelente para dependências que não mudam: uma versão específica de uma biblioteca fixada no seu hash exato. Seu limite é o espelho dessa força. Não faz nada pelos scripts destinados a se atualizar (a maioria das análises, gerenciadores de tags e SDKs de pagamento muda o arquivo servido com regularidade), e não faz nada pelos scripts carregados dinamicamente por outros scripts. Use SRI em todo lugar onde puder fixar uma versão. Não espere que ele cubra as partes da sua pilha que se atualizam sozinhas.
Passo 4: Reduza e revise a árvore de dependências
Cada script de terceiros na sua página é um caminho de injeção com todos os privilégios da página. A prevenção mais barata disponível é ter menos deles.
- Inventarie o que você realmente carrega. A maioria das equipes se surpreende com quantos scripts rodam numa página típica, e com quantos são carregados não diretamente, mas por outro script (um gerenciador de tags carregando um fornecedor que carrega outro fornecedor).
- Remova o que você não precisa. Um pixel de marketing sem uso ou uma ferramenta de teste A/B abandonada é puro risco sem benefício. Apagá-lo elimina um caminho de injeção por completo.
- Minimize o raio de impacto do gerenciador de tags. Uma conta de gerenciador de tags comprometida pode injetar "só mais uma tag" que parece rotineira. Restrinja quem pode publicar e revise o que publicam.
Este passo não tem desvantagem e nenhum controle por script pode substituí-lo: um script que não está na página não pode ser o que é comprometido.
Passo 5: Monitore o tempo de execução com inspeção da carga em sessões reais
Os passos de um a quatro reduzem a probabilidade. Nenhum deles consegue alcançar o caso que causa as piores violações do lado do cliente: um script em que você confia legitimamente, de uma origem que você permitiu legitimamente, que se torna malicioso depois de já estar carregado. A CSP o deixa passar porque a origem está permitida. O SRI o perde porque o script foi projetado para se atualizar. O seu próprio código está limpo. O único lugar que resta para pegá-lo é onde a injeção de fato chega, o navegador, no momento em que o script roda.
É isso que o monitoramento em tempo de execução faz. Ele constrói um inventário de cada script que roda em sessões reais de usuários, registra o que cada um faz e para onde envia dados, e alerta quando um script novo aparece, o código de um script conhecido muda, ou os dados começam a fluir para um destino inesperado. Esta é a camada que pega os skimmers do tipo Magecart e os ataques de fornecedores comprometidos que todos os controles preventivos acima deixam passar.
As ferramentas do lado do servidor não conseguem fornecer isso. Um firewall de aplicações web ou uma varredura do servidor vê o HTML que você entregou, não a carga que um fornecedor comprometido troca depois nem o código que uma extensão injeta na máquina do visitante. A detecção precisa ocorrer na camada do navegador, em sessões reais.
Onde a cside se encaixa: monitoramento de scripts em sessões reais
A cside é um único script JavaScript próprio (first-party) que fornece a camada de tempo de execução desta lista de verificação. Ela não substitui a sua CSP, a sua codificação de saída nem os seus hashes de SRI; é o controle que vigia o que os seus scripts permitidos realmente fazem uma vez que estão rodando, algo que nenhum dos outros consegue ver.
O monitoramento de scripts da cside inventaria cada script que roda em sessões reais do navegador, analisa a carga completa do script e alerta sobre domínios novos, código alterado e fluxos de dados inesperados. Funciona em dois modelos operacionais: o Script Method busca e analisa os scripts de terceiros do lado da cside antes que eles rodem na sessão, e o Scan Method para equipes que preferem uma pegada mais leve. É implantado como uma única tag de script a partir da sua própria origem, sem mudança de DNS; a cside busca e analisa os scripts de terceiros que a sua página carrega, mas não fica à frente do tráfego do seu site e não atua como um proxy.
Nas páginas de pagamento, esse inventário em tempo de execução também é um requisito de conformidade. Os requisitos 6.4.3 e 11.6.1 do PCI DSS 4.0.1 existem justamente porque o script injetado na página de pagamento é invisível para todos os controles do lado do servidor: 6.4.3 pede que você gerencie e autorize cada script nas suas páginas de pagamento, e 11.6.1 pede que você detecte alterações não autorizadas nesses scripts e nos cabeçalhos da página. O monitoramento de scripts em sessões reais é a resposta direta a ambos.
Lista de verificação de prevenção
A prevenção da injeção de JavaScript é em camadas, e as camadas não se sobrepõem:
- Codifique a saída e use sinks do DOM seguros para fechar a classe XSS no seu próprio código.
- Implante uma CSP rigorosa baseada em nonce para bloquear as origens de script não autorizadas.
- Adicione Subresource Integrity para fixar as dependências estáticas de terceiros.
- Reduza e revise a árvore de dependências para que haja menos a comprometer.
- Monitore sessões reais do navegador para que um script de confiança que se torna hostil seja pego no momento em que age.
Acerte os quatro primeiros e você terá fechado a maioria das portas. Adicione o quinto e você poderá ver a única porta que a prevenção nunca consegue trancar por completo: o fornecedor de confiança que é comprometido depois que você o deixou entrar.









