X-Frame-Options é um cabeçalho de resposta HTTP que informa ao navegador se sua página pode ser exibida dentro de um elemento <iframe>, <frame> ou <object>. Seu propósito principal é prevenir ataques de clickjacking, uma classe de ataques em que um site malicioso incorpora seu site invisivelmente e engana os visitantes para que executem ações não intencionais.
Apesar de ser um dos cabeçalhos de segurança mais antigos (introduzido por volta de 2009), o X-Frame-Options continua amplamente implantado e ainda fornece proteção importante. Vamos ver por que ele importa, como funciona e o que os proprietários de sites WordPress precisam saber.
Como ataques de clickjacking realmente funcionam
Clickjacking é surpreendentemente simples no conceito. Suponha que você administre um site WordPress com uma área de membros, e a página de perfil tenha um botão "Excluir minha conta". Um atacante cria uma página que parece um quiz ou jogo divertido. Ele incorpora sua página de perfil em um iframe invisível posicionado exatamente sobre o botão "Clique aqui para ver seu resultado".
Quando o visitante clica no que pensa ser um botão inofensivo na página do atacante, na verdade clica no botão "Excluir minha conta" da sua página oculta. Se o visitante estiver logado no seu site (e a maioria dos navegadores mantém cookies de sessão), a ação é executada. O visitante não tem ideia do que acabou de acontecer.
Essa técnica já foi usada em ataques reais para:
- Alterar configurações de privacidade em contas de redes sociais
- Autorizar aplicativos OAuth sem o conhecimento do usuário
- Clicar em anúncios para gerar receita fraudulenta
- Ativar acesso à webcam ou microfone em navegadores antigos
- Transferir dinheiro em interfaces de internet banking
O ataque funciona porque o navegador não tem uma forma nativa de saber que o iframe está sendo usado de forma maliciosa. O X-Frame-Options dá ao seu servidor uma maneira de optar por não ser enquadrado.
Valores do cabeçalho X-Frame-Options explicados
O cabeçalho suporta dois valores práticos (mais um terceiro descontinuado):
DENY: a página não pode ser exibida em nenhum frame, ponto final. Nenhum site, nem mesmo o seu, pode incorporar essa página em um iframe. Essa é a configuração mais estrita e funciona bem para páginas que jamais devem ser enquadradas, como páginas de login ou painéis administrativos.SAMEORIGIN: a página só pode ser exibida em um frame se o site que a enquadra tiver a mesma origem (mesmo protocolo, domínio e porta). Esse é o valor mais usado, porque permite que seu próprio site use iframes enquanto bloqueia sites externos.ALLOW-FROM uri: deveria permitir que você especificasse uma única origem permitida que pudesse enquadrar sua página. Porém, o suporte nunca foi consistente entre os navegadores. Chrome e Safari nunca o implementaram. O Firefox o suportou por algum tempo, mas removeu o suporte. Esse valor está, na prática, morto e não deve ser usado.
Quando usar DENY vs. SAMEORIGIN no WordPress
Para a maioria dos sites WordPress, SAMEORIGIN é a escolha certa. Eis o motivo:
O próprio WordPress usa iframes internamente em vários lugares. O Customizer de temas carrega uma prévia do seu site em um iframe. O uploader da biblioteca de mídia usa iframes. O diálogo "Inserir mídia" do editor clássico também. Se você definir DENY, todas essas funcionalidades vão quebrar, porque nem mesmo o seu próprio domínio terá permissão para enquadrar a página.
DENY faz sentido para páginas específicas onde o enquadramento jamais deve acontecer, como uma página de login isolada ou uma página de processamento de pagamento. Mas como padrão para todo o site no WordPress, SAMEORIGIN oferece a proteção necessária sem quebrar a funcionalidade administrativa.
Se você executa uma configuração WordPress headless em que a interface administrativa está em um domínio e o front-end em outro, será preciso ter cuidado extra aqui, porque SAMEORIGIN só permite enquadramento da mesma origem exata.
Por que ALLOW-FROM foi descontinuado
Vale a pena entender a descontinuação de ALLOW-FROM, pois ela ilustra um ponto mais amplo sobre cabeçalhos de segurança da web. A diretiva fazia parte da especificação original do X-Frame-Options, e a ideia era simples: permitir que um site dissesse "somente este domínio específico pode me enquadrar". Na prática, ela tinha vários problemas:
- Aceitava apenas um único URI, então você não podia liberar múltiplos domínios
- O Chrome nunca a implementou (o navegador mais usado do mundo simplesmente a ignorava)
- O Safari também nunca a suportou
- O comportamento era inconsistente entre os navegadores que a suportavam
A diretiva frame-ancestors do CSP foi projetada como o substituto adequado, suportando múltiplas origens e tendo comportamento consistente entre navegadores. Se você precisa permitir que domínios externos específicos incorporem suas páginas, frame-ancestors é a forma de fazer isso.
X-Frame-Options vs. frame-ancestors do CSP
A diretiva frame-ancestors do cabeçalho Content-Security-Policy tem o mesmo propósito do X-Frame-Options, mas com mais flexibilidade:
Content-Security-Policy: frame-ancestors 'self'Isso é equivalente a X-Frame-Options: SAMEORIGIN. Mas frame-ancestors também suporta múltiplas origens:
Content-Security-Policy: frame-ancestors 'self' https://trusted-partner.com https://another-domain.comIsso seria impossível somente com X-Frame-Options.
Quando ambos os cabeçalhos estão presentes, o comportamento dos navegadores varia. Navegadores modernos geralmente priorizam frame-ancestors sobre o X-Frame-Options. Ainda assim, a prática recomendada é enviar ambos os cabeçalhos para máxima compatibilidade. Navegadores antigos que não entendem CSP recorrerão ao X-Frame-Options, enquanto os modernos usarão frame-ancestors.
Aqui está um exemplo usando ambos juntos:
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'Configurando X-Frame-Options para WordPress
O WordPress, na verdade, envia X-Frame-Options: SAMEORIGIN por padrão para a área administrativa (wp-admin). Isso é feito no arquivo wp-includes/functions.php pela função send_frame_options_header(). Entretanto, o front-end do seu site não recebe esse cabeçalho por padrão.
Para adicioná-lo em todo o site via Apache, coloque isto no seu arquivo .htaccess:
Header always set X-Frame-Options "SAMEORIGIN"Para nginx:
add_header X-Frame-Options "SAMEORIGIN" always;Você também pode defini-lo via PHP no seu tema ou em um plugin:
add_action('send_headers', function() {
header('X-Frame-Options: SAMEORIGIN');
});Se você usa plugins que precisam incorporar seu site em um iframe em outro domínio (algumas ferramentas de testes A/B, widgets de chat ao vivo ou serviços de preview), pode ser preciso ajustar a abordagem. Nesse caso, use o frame-ancestors do CSP para liberar esses domínios específicos em vez de remover o X-Frame-Options por completo.
O que o InspectWP verifica
O InspectWP verifica se o seu site WordPress envia o cabeçalho X-Frame-Options nas páginas do front-end. Se o cabeçalho estiver ausente, o relatório o sinaliza como uma preocupação de segurança, porque qualquer site poderia incorporar suas páginas em um iframe, potencialmente possibilitando ataques de clickjacking contra seus visitantes. O relatório também indica o valor do cabeçalho quando presente, para que você possa verificar se está definido como SAMEORIGIN ou DENY, conforme apropriado para a sua configuração.