Introdução
Uma folha de dicas sobre XSS baseado em DOM é sua referência quando você quiser localizar, impedir e automatizar a proteção contra injeção de scripts no lado do cliente. Em essência: identifique onde a entrada controlada pelo usuário (um fonte) flui para uma API perigosa (a pia), substitua-o por padrões seguros (use textContent, createElementou sanitizadores) e integre as verificações em seu fluxo de trabalho de compilação, tempo de execução e pentest. Como o DOM XSS ocorre inteiramente no navegador e ignora muitos filtros tradicionais do lado do servidor, o front-end se torna a última linha de defesa - e o local onde a maioria das equipes ainda carece de automação.
O que é XSS baseado em DOM e por que é importante
De acordo com o PortSwigger, o cross-site scripting baseado em DOM surge quando "O JavaScript obtém dados de uma fonte controlável pelo invasor, como o URL, e os passa para um coletor que oferece suporte à execução de código dinâmico, como o eval() ou innerHTML." portswigger.net O OWASP DOM-based XSS Prevention Cheat Sheet destaca que a principal diferença em relação ao XSS armazenado/refletido é injeção no lado do cliente em tempo de execução. cheatsheetseries.owasp.org+1
Nos aplicativos modernos - aplicativos de página única (SPAs), uso intenso de widgets de terceiros, criação dinâmica de DOM - o risco aumenta: as cargas úteis podem nunca chegar aos registros do servidor, os WAFs tradicionais podem não percebê-las e os desenvolvedores geralmente não consideram suficientemente identificadores de fragmentos, fluxos postMessageou window.nametodas fontes comuns. Reconhecer essa mudança é o primeiro passo para medir sua maturidade de segurança.
Mapeamento da ameaça: fontes → sumidouros
A codificação segura começa com um mapa mental de fontes (onde a entrada do atacante entra) e pias (onde ocorre a execução). Um blog de segurança faz um resumo: "A fonte é qualquer lugar em uma página da Web onde a entrada do usuário pode ser adicionada... O coletor é para onde vão os dados inseridos na fonte... e, se não forem higienizados, podem levar a uma vulnerabilidade XSS baseada em DOM." Médio
Abaixo está uma tabela compacta que você deve manter em sua estação de trabalho e nas listas de verificação de revisão:
| Fonte (ponto de entrada) | Sink (API perigosa) |
|---|---|
| location.hash, location.search, URLSearchParams | innerHTML, insertAdjacentHTML, outerHTML |
| postMessage, window.name | eval(), new Function(), setTimeout(string) |
| document.referrer, localStorage, sessionStorage | setAttribute('on...'), element.src = ... |
| Dados de widgets de terceiros não testados | Qualquer inserção de DOM ou execução implícita de código |
Como enfatiza a OWASP, nenhuma técnica isolada evita o XSS: você deve combinar sumidouros adequados, codificação, sanitização e APIs seguras. cheatsheetseries.owasp.org
Padrões de ataque do mundo real com código e correções
Exemplo A - injeção de seletor jQuery por meio de fragmento de URL
Trecho vulnerável:
js
$(window).on('hashchange', () => {
const target = $(location.hash); // controlado pelo usuário
target[0].scrollIntoView();
});
Um invasor pode criar https://site/page.html#<img src="x" onerror="alert(1)"> - O jQuery trata o hash como HTML/seletor e o onerror acionadores.
Consertar:
js
const raw = location.hash.slice(1);
const safeId = /^[A-Za-z0-9_-]+$/.test(raw) ? raw : null;
Se (safeId) {
const target = document.getElementById(safeId);
Se (alvo) target.scrollIntoView();
}
Chave: validar, tratar o hash como identificador, não como HTML.
Exemplo B - postMessage → cadeia de avaliação
Trecho vulnerável:
js
window.addEventListener('message', e => {
eval(e.data); // perigoso
});
Versão corrigida:
window.addEventListener('message', event => {
if (event.origin !== '') return;
tente {
const msg = JSON.parse(event.data);
handleMessage(msg);
} catch {
console.warn('Formato de mensagem inválido');
}
});
Evitar avaliaçãoverificar a origem, usar análise segura.
Exemplo C - XSS de editor/visualização no contexto de markdown
Vulnerável:
js
preview.innerHTML = marked(userInput);
Seguro:
js
importar DOMPurify de 'dompurify';
const dirty = marked(userInput);
const clean = DOMPurify.sanitize(dirty);
preview.innerHTML = clean;
Ao permitir HTML gerado pelo usuário, a sanitização é necessária.
Implementações implantáveis: tornando-as operacionais
Ajudante DOM seguro - safe-dom.js
js
importar DOMPurify de 'dompurify';
função de exportação setSafeHTML(el, dirty) {
const clean = DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: ['b','i','a','p','ul','li','code','pre','img'],
ALLOWED_ATTR: ['href','src','alt','title','rel'],
FORBID_ATTR: ['onerror','onclick','style']
});
el.innerHTML = clean;
}
função de exportação setText(el, text) {
el.textContent = String(text ?? '');
}
função de exportação safeSetAttribute(el, name, val) {
Se (/^on/i.test(name)) lançar um novo erro('Atributo do manipulador de eventos não permitido');
el.setAttribute(name, String(val));
}
Use essa biblioteca para centralizar as operações seguras do DOM e reduzir os erros humanos.
Aplicação estática - amostra da ESLint
js
// .eslintrc.js
regras: {
'no-restricted-syntax': [
'erro',
{ selector: "AssignmentExpression[left.property.name='innerHTML']", message: "Use safe-dom.setSafeHTML ou textContent." },
{ selector: "CallExpression[callee.name='eval']", message: "Avoid eval()" },
{ selector: "CallExpression[callee.property.name='write']", message: "Evite document.write()" }
]
}
Combine com ganchos de pré-compromisso (husky, em estágio lint) para bloquear padrões perigosos.
Teste de CI / Puppeteer - GitHub Actions
.github/workflows/dom-xss.yml aciona um teste do Puppeteer:
// tests/puppeteer/dom-xss-test.js
js
const puppeteer = require('puppeteer');
(async ()=>{
const browser = await puppeteer.launch();
const page = await browser.newPage();
const logs = [];
page.on('console', msg => logs.push(msg.text()));
aguarde page.goto(${URL}/page.html#<img src="x" onerror="console.log("xss")">);
aguardar page.waitForTimeout(1000);
Se (logs.some(l=>l.includes('XSS'))) process.exit(2);
aguardar browser.close();
})();
Falha na construção da detecção.
Monitoramento do tempo de execução - MutationObserver
js
(função(){
const obs = new MutationObserver(muts=>{
muts.forEach(m=>{
m.addedNodes.forEach(n=>{
se(n.nodeType===1){
const html = n.innerHTML || '';
Se(/on(error|click|load)|<script\\b/i.test(html)){
navigator.sendBeacon('/_monitoring/xss', JSON.stringify({
url: location.href, snippet: html.slice(0,200)
}));
}
}
});
});
});
obs.observe(document.documentElement, {childList:true, subtree:true});
})();
Útil na preparação para alertar sobre injeções inesperadas de DOM.
Fortalecimento da segurança do navegador - CSP e tipos confiáveis
Cabeçalho do CSP:
pgsql
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-XYZ'; object-src 'none'; base-uri 'self';
Trecho de tipos confiáveis:
js
window.trustedTypes?.createPolicy('safePolicy', {
createHTML: s => { lança um novo erro('Atribuição direta de HTML bloqueada'); },
createScript: s => { lança um novo erro('Criação direta de script bloqueada'); }
});
Por padrão, isso nega os sinks não confiáveis.
Segurança de scripts de terceiros - SRI
bash
openssl dgst -sha384 -binary vendor.js | openssl base64 -A
Uso <script src="vendor.js" integrity="sha384‑..." crossorigin="anonymous"></script> para fixar e verificar.
Integração com a Penligent para automação
Se a sua equipe usa a Penligent, você pode elevar a proteção contra XSS do DOM para um pipeline contínuo. O artigo de pesquisa da Penligent observa como "Detecção de XSS baseado em DOM por meio de rastreamento de contaminação em tempo de execução... as técnicas do lado do servidor não conseguem detectar de forma confiável a injeção do lado do cliente." Penligente
Exemplo de fluxo de trabalho:
- No CI, acione uma varredura Penligent com o conjunto de regras
dom-xssfornecendo cargas úteis como#<img src="x" onerror="alert(1)">. - A Penligent executa fluxos sem cabeça, gera PoC e retorna os resultados por meio de webhook.
- O IC analisa as descobertas: se a gravidade for ≥ alta, reprovar a compilação e anotar o PR com a carga útil + coletor + recomendação de correção (por exemplo, "substituir
innerHTMLcomsafe-dom.setSafeHTML). - Os desenvolvedores corrigem, executam a CI novamente, mesclam somente quando estiver verde.
Isso fecha o ciclo: da referência (esta folha de consulta) → política de código → detecção automatizada → correção organizada.
Conclusão
O front-end não é mais "apenas a interface do usuário". Ele é uma superfície de ataque. Esta folha de dicas o orienta na compreensão da injeção no lado do cliente, na substituição de sumidouros perigosos, na criação de bibliotecas auxiliares seguras, na implantação da detecção estática/CI/runtime e na automação com uma plataforma como a Penligent. Próximas etapas: examine sua base de código em busca de sumidouros proibidos (innerHTML, avaliação), adote um domínio seguro aplicar regras de lint, integrar testes sem cabeça e lógica de pentest real, monitorar a produção/estágio e fixar recursos de terceiros. Proteger o DOM XSS é fazer com que ele impossível para passar, não confiando apenas no acaso.

