Um site criado por IA pode abrir checkout, agenda, rede social, documentação ou demonstração em uma nova aba. A experiência parece inofensiva: a pessoa clica, consulta o destino e mantém a página original aberta. O problema aparece quando a nova página conserva uma referência à aba que a abriu e consegue navegar essa aba para outro endereço.
Esse ataque é chamado de reverse tabnabbing. A aba externa usa window.opener para substituir a página original por uma imitação, uma tela de login ou outro destino enganoso. Quando a pessoa volta, pode acreditar que continua no site legítimo.
Navegadores modernos já tratam links com target="_blank" como se tivessem rel="noopener". Isso reduziu bastante o risco nos links HTML comuns. Ainda assim, uma revisão útil precisa localizar chamadas a window.open, nomes de janela reutilizados, links gerados por componentes e qualquer fluxo que dependa deliberadamente de window.opener.
Este guia mostra como fazer essa revisão sem transformar uma proteção moderna em alarme falso.
Entenda qual relação precisa ser cortada
Quando uma página abre outra janela, os dois contextos podem manter uma relação de abertura. Se o contexto novo recebe uma referência em window.opener, a política de mesma origem limita o que ele consegue ler, mas ainda permite algumas operações entre origens. Entre elas está navegar a propriedade location da janela original.
O fluxo de risco é simples:
- uma página confiável abre um destino externo;
- o destino externo recebe acesso ao
opener; - o destino altera a localização da aba original;
- a pessoa volta para uma página diferente sem perceber a troca.
O objetivo de noopener é remover essa ligação. Na janela aberta, window.opener passa a ser null.
A referência de rel="noopener" da MDN confirma tanto esse comportamento quanto a proteção implícita atual de target="_blank" em links, áreas e formulários.
Inventarie links, formulários e pop-ups
Comece pela fonte publicável, não apenas pela página que aparece no navegador:
rg -n \ 'target=["'"']_blank|window\.open\(|target=["'"'][^_][^"'"']*|window\.opener|rel=["'"'][^"'"']*opener' \ src publicAjuste os caminhos ao projeto e classifique cada ocorrência:
arquivo e linha:elemento ou função:destino fixo ou controlável:mesma origem ou origem externa:abre nova aba ou janela nomeada:precisa conversar com a janela original:proteção observada:Procure especialmente em:
- componentes genéricos de link;
- botões de checkout ou agendamento;
- links inseridos por CMS, Markdown ou dados de campanha;
- prévias abertas com
window.open; - formulários com
target="_blank"; - bibliotecas que criam pop-ups de login;
- código que lê ou escreve
window.opener.
Um componente pode esconder o HTML final. Confirme o DOM renderizado e a saída de build antes de concluir que todos os links estão protegidos.
Use noopener de forma explícita em links externos
Para um link HTML que deve abrir em nova aba e não precisa se comunicar com a página original, use:
<a href="https://parceiro.exemplo" target="_blank" rel="noopener"> Abrir parceiro </a>O noopener explícito documenta a intenção e protege ambientes que não aplicam o comportamento implícito moderno. Também ajuda revisores e ferramentas estáticas a distinguir um link deliberado de um esquecimento.
Não adicione noreferrer automaticamente. Ele também corta o opener, mas remove o header HTTP Referer. Isso pode alterar atribuição, Analytics e regras do destino. Use noreferrer apenas quando ocultar a página de origem fizer parte do requisito de privacidade.
| Configuração | Efeito principal |
|---|---|
target="_blank" moderno |
abre novo contexto e implica comportamento de noopener |
rel="noopener" |
corta a referência em window.opener e mantém o Referer normalmente |
rel="noreferrer" |
omite o Referer e também implica noopener |
| abrir na mesma aba | não cria a relação entre uma nova janela e a original |
manter opener de propósito |
exige protocolo entre janelas e uma revisão específica de postMessage |
Se abrir em nova aba não melhora a tarefa, prefira navegação normal. Menos contextos significam menos estado, menos surpresa para quem usa teclado ou leitor de tela e menos relações entre janelas para manter.
Proteja chamadas a window.open
Links não são o único caminho. Um script pode criar uma janela diretamente:
window.open("https://parceiro.exemplo", "_blank", "noopener");A opção noopener em windowFeatures impede o acesso à janela de origem. A documentação de window.open também registra que noreferrer ativa noopener, além de omitir o referrer.
Evite construir o destino com entrada não validada:
const destinations = { agenda: "https://agenda.exemplo", pagamento: "https://checkout.exemplo",};
const destination = destinations[action];
if (destination) { window.open(destination, "_blank", "noopener");}Uma URL vinda diretamente de query string, atributo editável ou resposta sem validação amplia o problema. Mesmo com noopener, a pessoa ainda pode ser enviada para um destino malicioso. A proteção corta o controle da aba original; ela não transforma uma URL desconhecida em confiável.
Trate janelas nomeadas como um caso próprio
Nem toda abertura usa _blank. Aplicações podem escolher nomes como checkout, preview ou oauth para reutilizar a mesma janela:
window.open(url, "checkout");Esse desenho cria estado entre cliques e pode manter uma referência enquanto a janela navega por diferentes origens. Não aplique uma troca mecânica sem entender o fluxo.
Para cada janela nomeada, responda:
- por que a reutilização é necessária;
- quais origens podem ocupar a janela;
- se a página original envia ou recebe dados;
- como o código reage quando a janela foi fechada;
- se um redirecionamento muda a origem esperada;
- quais dados seriam expostos se o destino fosse substituído.
Se a comunicação é necessária, use um protocolo mínimo com postMessage, origem exata e validação de dados. O guia de uso seguro de postMessage cobre esse contrato. Não mantenha opener apenas por conveniência.
Revise componentes e conteúdo controlável
Sites gerados por IA costumam receber listas de parceiros, depoimentos, cards e CTAs como dados. O componente central precisa aplicar a política de link, não cada página separadamente.
Uma regra possível é:
destino interno -> mesma abadestino externo aprovado -> nova aba com noopener, se necessáriodestino vindo de usuário -> validar protocolo e host antes de renderizarprotocolo javascript:, data: ou desconhecido -> rejeitarNão valide host com busca de substring. parceiro.exemplo.atacante.com contém o texto esperado, mas é outro domínio. Construa um objeto URL, exija https: quando aplicável e compare o origin completo com uma allowlist.
Links criados por Markdown ou CMS precisam passar pela mesma política. Verifique tanto a etapa de transformação quanto o HTML final.
O guia de scripts de terceiros ajuda a revisar outra forma comum de confiança externa. Links e scripts não têm o mesmo poder, mas ambos ampliam a superfície quando destinos são aceitos sem inventário.
Teste sem depender de um ataque real
Faça os testes apenas em origens que você controla. Crie uma página simples de destino que mostre o estado do opener sem tentar redirecionar ninguém:
<output id="resultado"></output><script> document.querySelector("#resultado").textContent = window.opener === null ? "opener ausente" : "opener presente";</script>Depois:
- abra cada link externo em um navegador compatível com sua matriz;
- confirme que a página de teste informa
opener ausente; - inspecione o DOM para conferir
targeterel; - teste links gerados por dados, CMS e Markdown;
- acione cada caminho de
window.open; - repita após redirects controlados;
- valide teclado, foco e anúncio da abertura em nova aba.
Não desative proteções do navegador de uma pessoa nem use um domínio externo como laboratório. O objetivo é demonstrar a ausência da relação, não reproduzir phishing.
Ferramentas estáticas podem ajudar, mas não substituem o teste do HTML final. Uma busca encontra padrões conhecidos; o navegador revela o comportamento efetivo depois da renderização.
Publique e verifique a URL real
O contrato de Publicação envia o HTML, o JavaScript e os assets preparados pelo projeto. Ele não reescreve links externos nem adiciona noopener a chamadas criadas pelo código da página.
Antes de publicar:
- inventarie os destinos externos;
- remova aberturas desnecessárias em nova aba;
- declare
noopenernos links que permanecerem; - proteja
window.opene valide URLs dinâmicas; - teste o pacote final localmente;
- registre qualquer fluxo que dependa de janelas conectadas.
Depois de publicar, abra a URL padrão e cada domínio próprio ativo. Embora a proteção de opener não dependa da marca do domínio, a classificação entre link interno e externo pode mudar quando o host final muda.
Se encontrar um componente vulnerável, corrija a fonte, gere o build novamente e republique o mesmo Site para manter a URL já aprovada.
Prompt copiável para o agente
Revise links externos e aberturas de janela deste site antes de publicar.
Pasta publicável: [caminho]URL de teste: [URL]URL padrão: [URL]Domínios próprios: [lista]Destinos externos aprovados: [lista]
- Inventarie target="_blank", targets nomeados, formulários e window.open.- Localize leituras ou escritas de window.opener.- Não marque links modernos como vulneráveis só porque rel="noopener" está implícito.- Prefira noopener explícito quando não houver comunicação entre janelas.- Não adicione noreferrer sem avaliar referrer e Analytics.- Valide protocolo e origin de URLs dinâmicas por allowlist exata.- Preserve somente os fluxos que realmente precisam de uma janela conectada.- Teste o HTML final com uma página controlada que apenas reporte window.opener.- Não execute redirecionamentos de phishing nem teste domínios sem autorização.- Entregue ocorrências, correções, testes e riscos restantes.Checklist final
- links, formulários e chamadas a
window.openforam inventariados; - destinos externos e controláveis estão identificados;
- novas abas desnecessárias foram removidas;
- links restantes usam proteção de
openercompatível com a matriz; -
noreferrersó foi usado quando a omissão do referrer é intencional; - URLs dinâmicas usam protocolos e origens aprovados;
- janelas nomeadas têm comportamento documentado;
- qualquer comunicação entre janelas valida origem e dados;
- o HTML final foi testado em origem controlada;
- URL padrão e domínios próprios foram verificados;
- a Version publicada corresponde aos arquivos revisados.
Reverse tabnabbing não deve ser tratado como se todo target="_blank" moderno fosse vulnerável. A revisão correta reconhece a proteção implícita atual, torna a intenção explícita e concentra esforço nos caminhos que ainda criam ou preservam uma relação entre janelas.
