Codificação Segura para Desenvolvedores JavaScript
O JavaScript é executado em qualquer lugar onde haja um navegador, o que o torna uma das superfícies mais atacadas no desenvolvimento de software. A maioria das vulnerabilidades não vem de explorações exóticas—elas vêm de padrões previsíveis no código do dia a dia. Este guia cobre as práticas de codificação segura em JavaScript que mais importam quando seu código é executado diretamente no navegador.
Principais Pontos
- XSS baseado em DOM é uma das vulnerabilidades JavaScript mais comuns—evite passar dados não confiáveis para destinos como
innerHTML,eval()oudocument.write() - Use
textContentem vez deinnerHTMLpara dados fornecidos pelo usuário, e sanitize com DOMPurify quando realmente precisar renderizar HTML - Nunca use
eval(),Function()ousetTimeout/setIntervalbaseados em string com entrada dinâmica - Implemente Content Security Policy (CSP) com nonces ou hashes e combine-o com Trusted Types para defesa em profundidade
- Evite armazenar tokens de autenticação ou segredos em
localStorageousessionStorage—prefira cookiesHttpOnlypara identificadores de sessão - Sempre valide
event.originao manipular eventospostMessage - Audite sua árvore de dependências regularmente e use Subresource Integrity para scripts carregados via CDN
XSS Baseado em DOM: Uma das Vulnerabilidades JavaScript Mais Comuns
Prevenir XSS em JavaScript começa com o entendimento de onde ele realmente se origina. XSS baseado em DOM acontece quando seu código lê de uma fonte controlada pelo atacante—como location.hash, document.referrer ou URLSearchParams—e a escreve na página de forma insegura.
A regra fundamental: nunca passe dados não confiáveis para um destino que interpreta HTML ou executa código.
Destinos inseguros a evitar com dados dinâmicos:
// ❌ Todos estes podem executar scripts injetados
element.innerHTML = userInput
element.outerHTML = userInput
document.write(userInput)
eval(userInput)
setTimeout(userInput, 0) // apenas na forma de string
new Function(userInput)()
Alternativas seguras:
// ✅ Trata o conteúdo como texto, nunca como marcação
element.textContent = userInput
element.innerText = userInput
Quando você realmente precisar renderizar HTML—um editor de texto rico, por exemplo—sanitize primeiro com DOMPurify:
// ✅ Renderização segura de rich-text
element.innerHTML = DOMPurify.sanitize(userInput)
A mesma lógica se aplica a setAttribute. Atributos dinâmicos como href, src e manipuladores de eventos (onclick, onload) podem executar JavaScript. Mantenha-se em atributos estáticos e não executáveis ao trabalhar com valores fornecidos pelo usuário.
Execução Dinâmica de Código É Sempre Insegura
eval(), Function() e setTimeout/setInterval baseados em string não são apenas má prática—são vetores diretos de injeção de código. Não há maneira segura de usá-los com entrada não confiável.
Se você está analisando dados, use JSON.parse(). Se você precisa de comportamento dinâmico, use estruturas de dados e lógica explícita em vez de geração de código em tempo de execução.
Content Security Policy como Camada de Defesa
Content Security Policy (CSP) limita quais scripts podem ser executados e de onde podem ser carregados. Uma CSP rigorosa usando nonces ou hashes—em vez de 'unsafe-inline'—reduz significativamente o raio de explosão de qualquer XSS que passe despercebido.
Combine CSP com Trusted Types em navegadores compatíveis para impor escritas DOM seguras no nível da API. O suporte dos navegadores para Trusted Types continua a expandir e pode ser acompanhado em webstatus.dev.
Discover how at OpenReplay.com.
APIs Seguras do Navegador: Cookies e Armazenamento Client-Side
Cookies que contêm identificadores de sessão devem sempre ter HttpOnly (bloqueia acesso JavaScript), Secure (apenas HTTPS) e SameSite=Strict ou Lax (ajuda a mitigar CSRF). Configurá-los no lado do servidor é mais confiável do que fazê-lo a partir do JavaScript.
localStorage e sessionStorage são acessíveis a qualquer script na página. Evite armazenar tokens de autenticação, segredos de sessão ou dados sensíveis do usuário lá—uma vulnerabilidade XSS expõe imediatamente tudo no armazenamento.
Mensagens Cross-Origin com postMessage
postMessage é útil, mas fácil de usar incorretamente. Sempre valide a origin das mensagens recebidas antes de agir sobre seus dados:
window.addEventListener('message', (event) => {
// ✅ Sempre valide a origem antes de processar
if (event.origin !== 'https://trusted-origin.com') return
handleMessage(event.data)
})
Ao enviar mensagens, evite usar '*' como targetOrigin a menos que você realmente não tenha um destino fixo. No lado receptor, sempre valide event.origin para garantir que a mensagem veio de um site confiável. Mais detalhes sobre uso seguro são abordados na documentação do postMessage.
Segurança da Cadeia de Suprimentos JavaScript
A segurança da cadeia de suprimentos JavaScript é uma preocupação crescente. Um único pacote comprometido ou malicioso pode afetar milhares de aplicações. Passos práticos:
- Execute
npm auditou use Snyk para detectar vulnerabilidades conhecidas nas dependências - Faça commit do seu arquivo de lock (
package-lock.jsonouyarn.lock) e trate mudanças inesperadas como um sinal de alerta - Use hashes de Subresource Integrity (SRI) para quaisquer scripts carregados de uma CDN
- Audite novos pacotes antes de adicioná-los—verifique contagens de download, atividade de manutenção e se o nome do pacote pode ser um typosquat
Referência Rápida: Padrões Seguros vs. Inseguros
| Inseguro | Alternativa Segura |
|---|---|
innerHTML = userInput | textContent = userInput |
eval(str) | JSON.parse(str) |
setTimeout(str, n) | setTimeout(fn, n) |
Token em localStorage | Cookie HttpOnly |
message sem verificação de origem | Valide event.origin primeiro |
Conclusão
A maioria das vulnerabilidades JavaScript segue o mesmo padrão: dados não confiáveis alcançam uma API poderosa sem validação. Crie o hábito de perguntar “de onde vem esse valor e o que essa API pode fazer com ele?” Essa pergunta, aplicada consistentemente, detecta a maioria dos problemas antes que sejam lançados.
Perguntas Frequentes
Nem sempre, mas é inseguro sempre que você passa dados que se originam de entrada do usuário ou qualquer fonte externa. Se você precisar renderizar HTML dinâmico, sanitize-o primeiro com uma biblioteca como DOMPurify. Para conteúdo de texto simples, use textContent, que nunca interpreta marcação ou executa scripts.
localStorage é acessível a qualquer JavaScript executado na página. Se um atacante explorar uma vulnerabilidade XSS, ele pode ler tudo no armazenamento, incluindo seus tokens. Cookies HttpOnly são uma escolha mais segura porque o JavaScript não pode acessá-los de forma alguma, o que limita o dano de ataques client-side.
XSS refletido envolve um payload malicioso enviado ao servidor e ecoado de volta na resposta. XSS baseado em DOM nunca chega ao servidor. Em vez disso, o JavaScript client-side lê de uma fonte controlada pelo atacante, como o fragmento da URL, e o escreve na página de forma insegura. Ambos são perigosos, mas XSS baseado em DOM é mais difícil de detectar no lado do servidor.
CSP informa ao navegador quais fontes de script têm permissão para executar. Uma política rigorosa usando nonces ou hashes bloqueia scripts inline e scripts externos não autorizados de serem executados. Mesmo que um atacante injete marcação maliciosa na página, o navegador se recusa a executá-la porque não corresponde à política.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.