Clickjacking: prevenção com X-Frame-Options e CSP
1. O que é Clickjacking e por que devs devem se importar?
Clickjacking, também conhecido como "UI redressing", é uma técnica de ataque onde um invasor sobrepõe uma página web legítima dentro de um iframe transparente, enganando o usuário a clicar em elementos invisíveis. O usuário acredita estar interagindo com a página visível, mas na verdade está clicando em botões, links ou formulários da página alvo oculta.
Um exemplo clássico é o "likejacking": o invasor cria uma página atraente (como um quiz viral) e, por trás dela, posiciona o botão "Curtir" do Facebook. Quando o usuário clica em qualquer lugar da página visível, está na verdade curtindo uma página ou publicando conteúdo sem consentimento.
Para desenvolvedores, as consequências são graves:
- Responsabilidade legal por ações não autorizadas realizadas em nome dos usuários
- Perda de confiança quando dados sensíveis são comprometidos
- Violação de regulamentações como LGPD e GDPR
- Danos à reputação da aplicação e da empresa
2. Anatomia de um ataque Clickjacking
O ataque utiliza três elementos principais: um iframe apontando para a página alvo, posicionamento absoluto para sobreposição, e transparência total. Eis um exemplo simplificado:
<!DOCTYPE html>
<html>
<head>
<title>Quiz Interativo</title>
<style>
iframe {
position: absolute;
top: 0;
left: 0;
width: 500px;
height: 500px;
opacity: 0;
z-index: 10;
}
.botao-falso {
position: absolute;
top: 200px;
left: 150px;
width: 200px;
height: 50px;
background: #007bff;
color: white;
text-align: center;
line-height: 50px;
z-index: 1;
}
</style>
</head>
<body>
<div class="botao-falso">Clique aqui para ganhar!</div>
<iframe src="https://banco-exemplo.com/transferir"></iframe>
</body>
</html>
O invasor alinha o botão falso exatamente sobre o botão "Confirmar Transferência" da página do banco. O usuário vê apenas o botão azul convidativo, mas seu clique é capturado pelo iframe transparente, executando a ação maliciosa.
3. Primeira linha de defesa: X-Frame-Options
O cabeçalho HTTP X-Frame-Options foi a primeira defesa padronizada contra clickjacking. Ele informa ao navegador se a página pode ser exibida dentro de um iframe.
Valores disponíveis:
- DENY: a página nunca pode ser exibida em iframe
- SAMEORIGIN: permitido apenas na mesma origem
- ALLOW-FROM uri: permitido apenas para uma URI específica (obsoleto e não suportado em navegadores modernos)
Implementação em servidores comuns:
# Apache - arquivo .htaccess ou httpd.conf
Header always set X-Frame-Options "SAMEORIGIN"
# Nginx - arquivo de configuração
add_header X-Frame-Options "SAMEORIGIN" always;
# Express.js (Node.js)
app.use((req, res, next) => {
res.setHeader('X-Frame-Options', 'DENY');
next();
});
# Django - settings.py
MIDDLEWARE = [
'django.middleware.clickjacking.XFrameOptionsMiddleware',
# ...
]
X_FRAME_OPTIONS = 'DENY'
Limitações importantes:
- ALLOW-FROM é obsoleto e ignorado pelo Chrome e Firefox
- Não permite listar múltiplas origens confiáveis
- Suporte descontinuado em navegadores modernos que priorizam CSP
4. Defesa moderna e flexível: Content-Security-Policy (CSP)
A diretiva frame-ancestors do CSP substitui e expande as capacidades do X-Frame-Options. Ela define quais origens podem embutir a página em iframes, frames ou objetos.
Sintaxe básica:
# Bloquear todos os iframes
Content-Security-Policy: frame-ancestors 'none'
# Permitir apenas mesma origem
Content-Security-Policy: frame-ancestors 'self'
# Permitir origens específicas
Content-Security-Policy: frame-ancestors https://meudominio.com https://parceiro.com
# Permitir qualquer origem (não recomendado)
Content-Security-Policy: frame-ancestors *
Comparação com X-Frame-Options:
| Característica | X-Frame-Options | CSP frame-ancestors |
|---|---|---|
| Múltiplas origens | Não | Sim |
| Suporte moderno | Parcial | Completo |
| Granularidade | Baixa | Alta |
| Relatórios de violação | Não | Sim (com report-uri) |
Exemplo de cabeçalho completo para um cenário típico:
Content-Security-Policy:
default-src 'self';
frame-ancestors 'self' https://api.parceiro.com;
report-uri /csp-violation;
5. Implementação prática no código do desenvolvedor
Node.js com Express usando Helmet.js
const helmet = require('helmet');
const express = require('express');
const app = express();
// Helmet configura X-Frame-Options e CSP automaticamente
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
frameAncestors: ["'self'", "https://trusted-app.com"],
},
},
}));
Python com Flask usando django-csp
# settings.py
INSTALLED_APPS = [
'csp',
# ...
]
MIDDLEWARE = [
'csp.middleware.CSPMiddleware',
# ...
]
CSP_DEFAULT_SRC = ("'self'",)
CSP_FRAME_ANCESTORS = ("'self'", "https://widgets.externos.com")
Java com Spring Security
// SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.headers()
.frameOptions().sameOrigin()
.and()
.headers()
.contentSecurityPolicy("frame-ancestors 'self' https://app.externo.com");
}
}
Testando a proteção
# Teste com curl
curl -I https://meusite.com/pagina-segura
# Resposta esperada:
# HTTP/2 200
# content-security-policy: frame-ancestors 'self'
# x-frame-options: SAMEORIGIN
No navegador, abra o DevTools (F12), vá para a aba Network e verifique os cabeçalhos de resposta. Para testar se a proteção funciona, crie uma página HTML local com um iframe apontando para seu site e veja se o carregamento é bloqueado.
6. Casos especiais e armadilhas comuns
Clickjacking em SPAs e iframes legítimos
Single Page Applications frequentemente precisam de iframes para widgets de terceiros (YouTube, Stripe, Google Maps). Nesses casos, configure frame-ancestors com as origens específicas dos provedores, nunca use *.
# Permitindo iframes do YouTube e Stripe
Content-Security-Policy: frame-ancestors 'self' https://www.youtube.com https://js.stripe.com;
Erros frequentes
- Usar ALLOW-FROM — obsoleto, não funciona no Chrome
- Esquecer páginas de login — são o alvo principal de clickjacking
- Conflito com CSP inline — se usa `script-src 'unsafe-inline'', revise a política
- Widgets quebrados — testar exaustivamente após aplicar restrições
Solução para widgets de terceiros
# Para formulários Stripe, a Stripe já fornece documentação específica
# Configure frame-ancestors com a URL exata do seu domínio
Content-Security-Policy: frame-ancestors 'self' https://checkout.stripe.com;
7. Estratégia de defesa em camadas
Combinação de X-Frame-Options + CSP
Para máxima compatibilidade com navegadores antigos, use ambos os cabeçalhos. Quando ambos estão presentes, o navegador prioriza o CSP.
# Cabeçalhos duplos (redundância segura)
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'
Framebusting JavaScript (técnica complementar)
Embora defensas HTTP sejam preferíveis, o framebusting serve como fallback:
<script>
if (window.top !== window.self) {
window.top.location = window.self.location;
}
</script>
Atenção: invasores podem contornar framebusting com sandbox ou onbeforeunload. Use apenas como camada adicional, nunca como defesa principal.
Monitoramento com CSP-Report-Only
Antes de aplicar restrições severas, use o modo de relatório para identificar violações sem bloquear:
Content-Security-Policy-Report-Only:
frame-ancestors 'self';
report-uri /csp-violation-endpoint;
Checklist final para o dev antes do deploy
- [ ] X-Frame-Options configurado (DENY ou SAMEORIGIN)
- [ ] CSP frame-ancestors definido com origens específicas
- [ ] Páginas de login e formulários sensíveis testados
- [ ] Widgets de terceiros funcionando com as restrições
- [ ] CSP-Report-Only ativo em staging para detectar problemas
- [ ] Teste manual com página HTML contendo iframe
- [ ] Ferramentas de segurança (OWASP ZAP, observatory.mozilla.org) sem alertas críticos
Referências
- MDN Web Docs: X-Frame-Options — Documentação completa sobre o cabeçalho X-Frame-Options, valores suportados e exemplos de implementação
- MDN Web Docs: CSP frame-ancestors — Referência oficial da diretiva frame-ancestors, sintaxe e exemplos práticos
- OWASP: Clickjacking Defense Cheat Sheet — Guia completo de defesas contra clickjacking, incluindo X-Frame-Options, CSP e framebusting
- Helmet.js Documentation — Middleware de segurança para Express.js, incluindo configuração de X-Frame-Options e Content-Security-Policy
- Content Security Policy (CSP) Quick Reference Guide — Guia de referência rápida para todas as diretivas CSP, com exemplos e gerador de políticas
- Mozilla Observatory — Ferramenta online para testar a segurança de cabeçalhos HTTP, incluindo proteção contra clickjacking
- PortSwigger: Clickjacking — Artigo técnico do criador do Burp Suite sobre exploração e prevenção de clickjacking