Um site criado por IA pode ter um formulário, um botão de compra, uma autorização ou uma área autenticada com ações sensíveis. Se outra página consegue carregar esse conteúdo em um iframe e posicionar camadas visuais por cima, a pessoa pode acreditar que está clicando em uma interface enquanto ativa outra. Esse tipo de redirecionamento de interface é clickjacking.
Prevenir clickjacking começa por decidir quem pode incorporar cada página. A proteção principal é entregue em headers de resposta, especialmente a diretiva CSP frame-ancestors, com X-Frame-Options como compatibilidade adicional quando necessário. CSS, JavaScript de “frame busting” e a aparência da página não substituem essa fronteira.
Este guia mostra como mapear o risco, escolher uma política, testar incorporação de forma autorizada e verificar a URL realmente publicada.
Entenda o ataque sem reduzir tudo a um botão
Clickjacking também é chamado de UI redress: a página maliciosa reorganiza a percepção visual para que a ação real não corresponda ao que a pessoa enxerga. O alvo pode aparecer transparente, deslocado ou coberto por elementos que orientam o clique.
O risco cresce quando a página incorporada possui:
- sessão autenticada enviada automaticamente;
- ação de compra, assinatura ou transferência;
- alteração de e-mail, domínio, permissões ou configuração;
- formulário que envia dados;
- pedido de acesso a câmera, microfone ou geolocalização;
- gesto que inicia download, compartilhamento ou autorização;
- ação destrutiva com confirmação fraca.
Uma landing puramente informativa tem impacto menor, mas ainda pode conter formulários, widgets e links de conversão. Não classifique pelo rótulo “site estático”; classifique pelas ações que a pessoa consegue executar e pelas integrações chamadas no navegador.
A visão geral de clickjacking da MDN explica que o ataque depende de incorporar o alvo em um contexto como iframe. Por isso, restringir framing é a defesa central.
Mapeie onde a incorporação é requisito
Antes de definir none ou self, descubra se alguma página precisa aparecer dentro de outro produto:
rg -n '<iframe|frame-ancestors|X-Frame-Options|postMessage|window\.parent|window\.top|frameElement' \ src publicDepois liste consumidores externos conhecidos:
rota:ação sensível:pode ser incorporada:origens autorizadas:quem controla essas origens:sessão ou cookies envolvidos:postMessage envolvido:política esperada:evidência na resposta publicada:Não confunda duas direções:
frame-srccontrola de onde sua página pode carregar frames;frame-ancestorscontrola quem pode carregar sua página dentro de um frame.
Para prevenir clickjacking contra sua página, a diretiva relevante é frame-ancestors.
Negue framing quando não houver um caso de uso
Se nenhuma origem deve incorporar a página, use:
Content-Security-Policy: frame-ancestors 'none'A documentação de CSP da MDN recomenda 'none' quando o site não precisa ser incorporado. Essa política impede inclusive frames na própria origem.
Se apenas documentos da mesma origem precisam incorporar:
Content-Security-Policy: frame-ancestors 'self'Para parceiros específicos, declare origens exatas:
Content-Security-Policy: frame-ancestors 'self' https://portal.exampleEvite curingas amplos. Autorizar todos os subdomínios transfere a segurança para cada aplicação e cada takeover possível naquele namespace. Liste apenas as origens que possuem necessidade, proprietário e processo de remoção.
frame-ancestors não herda de default-src. A diretiva precisa aparecer explicitamente na política entregue.
Use X-Frame-Options como camada de compatibilidade
X-Frame-Options possui opções menos flexíveis:
X-Frame-Options: DENYou:
X-Frame-Options: SAMEORIGINNão use ALLOW-FROM; a opção é obsoleta e não oferece uma allowlist moderna interoperável. Para permitir parceiros específicos, use frame-ancestors.
Quando os dois headers aparecem, navegadores modernos que suportam frame-ancestors seguem a CSP. O Clickjacking Defense Cheat Sheet da OWASP apresenta os headers como mecanismos principais e alerta que scripts simples de frame breaking podem ser contornados.
Escolha valores coerentes. Não envie X-Frame-Options: DENY junto com uma CSP que autoriza um portal sem documentar o comportamento esperado em navegadores legados. A política precisa refletir a matriz de compatibilidade real, não uma coleção de snippets acumulados.
Não coloque frame-ancestors em meta tag
Uma CSP em <meta http-equiv="Content-Security-Policy"> pode aplicar parte das diretivas, mas não frame-ancestors. A diretiva precisa vir no header HTTP da resposta que entrega o documento protegido.
Isto não funciona como proteção contra framing:
<meta http-equiv="Content-Security-Policy" content="frame-ancestors 'none'" />Também não confie em:
if (top !== self) { top.location = self.location;}Além de criar problemas de navegação e compatibilidade, scripts desse tipo executam tarde e podem ser bloqueados ou contornados. Trate JavaScript somente como defesa adicional para cenários legados específicos, nunca como substituto do header.
O guia de Content Security Policy ajuda a manter frame-ancestors dentro de uma política revisada sem confundi-la com as diretivas de scripts, estilos e conexões.
Trate cookies e confirmações como defesa em profundidade
Cookies de sessão com SameSite=Lax ou SameSite=Strict podem deixar de acompanhar certas requisições em contexto cross-site, reduzindo parte do impacto. Isso não impede que a página seja incorporada nem cobre interfaces sem cookie.
Da mesma forma, uma confirmação explícita para ações críticas ajuda a pessoa a perceber contexto e consequência, mas não corrige framing aberto. Use confirmações proporcionais ao risco, validação no servidor, reautenticação quando necessária e proteção contra CSRF conforme o contrato da aplicação.
Não transforme um problema de origem em um problema de cor de botão. Espaçamento, atraso, hover e z-index podem dificultar uma demonstração específica, mas o atacante controla a composição da página externa.
Revise postMessage em páginas que precisam ser incorporadas
Uma página incorporável costuma trocar dados com o pai usando postMessage. Nesse caso, a allowlist de frame-ancestors é apenas uma parte do contrato.
Ao receber mensagens:
const trustedOrigins = new Set(["https://portal.example"]);
window.addEventListener("message", (event) => { if (!trustedOrigins.has(event.origin)) return; if (!isExpectedMessage(event.data)) return;
handleMessage(event.data);});Valide event.origin, formato, tipo e tamanho. Não use * como destino ao enviar dados sensíveis. Confirme também a janela esperada quando o fluxo exigir. Uma origem autorizada a incorporar não deve receber comandos ilimitados sobre a página.
Se a necessidade de embed desapareceu, remova tanto a allowlist quanto o canal de mensagens. Permissões antigas costumam sobreviver mais tempo que a integração que as justificou.
Teste a política em uma origem diferente
Crie uma página de teste local ou em um ambiente autorizado que tente incorporar a URL:
<!doctype html><html lang="pt-BR"> <body> <iframe src="https://exemplo.sitenoar.app" title="Teste autorizado de incorporação" width="900" height="600" ></iframe> </body></html>Sirva o arquivo em outra origem e observe Console e Network. Para uma política que nega framing, a evidência esperada é o navegador recusar a renderização por frame-ancestors ou X-Frame-Options.
Não teste contra sistemas de terceiros sem autorização e não sobreponha botões reais para “provar impacto”. O objetivo é validar a fronteira de incorporação com uma página inofensiva.
Teste também cada origem autorizada. Uma política restritiva que bloqueia o ataque, mas quebra o portal legítimo, ainda está incorreta. Quando houver caminhos autenticados, use contas e dados de teste.
Inspecione a resposta publicada
O contrato de Publicação envia os arquivos estáticos preparados pelo projeto. No contrato atual, um arquivo _headers dentro do pacote não é convertido em headers HTTP. Portanto, a presença de frame-ancestors no repositório ou na build não demonstra proteção.
Verifique cada documento relevante:
curl -sS -D - -o /dev/null https://exemplo.sitenoar.app/acao \ | rg -i '^(content-security-policy|x-frame-options):'Depois tente o iframe autorizado. Faça isso na URL padrão e em cada domínio próprio ativo. A política deve ser aplicada à resposta do documento, e um hostname adicional precisa de evidência própria.
Se a camada atual não permite configurar os headers, registre o controle como pendência bloqueante para páginas com ações sensíveis. Não marque a correção como concluída com meta tag, JavaScript ou arquivo de configuração não interpretado.
Inclua a proteção no ciclo de republicação
Uma mudança de produto pode introduzir um novo embed legítimo; uma integração removida pode deixar uma origem autorizada sem necessidade. Por isso, frame-ancestors precisa de proprietário e revisão.
Antes de publicar:
- classifique rotas e ações sensíveis;
- documente origens que realmente incorporam;
- defina
none,selfou allowlist mínima; - alinhe
X-Frame-Optionscom a compatibilidade desejada; - revise cookies, confirmações e
postMessagecomo camadas adicionais; - teste origem bloqueada e origens permitidas;
- inspecione headers no ambiente que controla a resposta.
Depois de republicar, repita o teste externo. Uma build aprovada não prova que a camada pública entregou a mesma política.
Prompt copiável para o agente
Revise este site para risco de clickjacking sem alterar headers automaticamente.
Aplicação publicável: [caminho]URLs autorizadas para teste: [lista]Origens que precisam incorporar: [lista ou nenhuma]
- Classifique páginas, ações sensíveis, sessão e cookies envolvidos.- Inventarie iframes, postMessage, window.parent e integrações de embed.- Diferencie frame-src de frame-ancestors.- Proponha 'none', 'self' ou a menor allowlist explícita.- Revise X-Frame-Options sem usar ALLOW-FROM.- Não use meta CSP ou frame-busting como proteção principal.- Teste incorporação em origem diferente e em cada origem permitida.- Verifique os headers em cada URL publicada.- Não trate arquivo _headers como ativo sem prova na resposta.- Devolva política proposta, evidências, compatibilidade e riscos restantes.Checklist final
- páginas e ações sensíveis foram classificadas;
- necessidade de incorporação foi confirmada por rota;
- origens permitidas têm proprietário e justificativa;
-
frame-ancestorsaparece explicitamente na política; - allowlist não usa curinga amplo sem necessidade;
-
X-Frame-Optionsé coerente com a CSP; -
ALLOW-FROMnão foi usado; - meta CSP e frame-busting não são a defesa principal;
- cookies e confirmações são apenas defesa adicional;
- mensagens validam origem e formato;
- origem externa bloqueada foi testada;
- embeds legítimos continuaram funcionando;
- headers foram inspecionados na URL padrão e nos domínios próprios;
- política possui responsável e data de revisão.
Clickjacking explora a distância entre o que a pessoa vê e o que o navegador realmente ativa. Quando a equipe controla quem pode incorporar cada documento e mede a política na resposta pública, essa distância deixa de depender da aparência e passa a ter uma fronteira verificável.
