X-Frame-Options必须由服务端通过HTTP响应头发送,因<meta>标签自2015年起已被主流浏览器废弃;Content-Security-Policy的frame-ancestors更可靠,支持多源白名单且优先级更高;前端JS检测top !== self无效且危险,无法防御点击劫持。

为什么X-Frame-Options响应头必须由服务端发
浏览器只认HTTP响应头,<meta http-equiv="X-Frame-Options">在2015年后所有主流浏览器(Chrome、Firefox、Safari、Edge)都已废弃,写了等于没写。你用curl -I https://yoursite.com或开发者工具Network面板查响应头,根本看不到它。
攻击者构造恶意页面时,直接<iframe src="https://yoursite.com"></iframe>加载你的HTML,根本不会经过你页面里的<meta>解析阶段——安全控制点早就错过了。
- 必须在Nginx里写
add_header X-Frame-Options "DENY" always;(always很关键,否则304或错误页可能漏发) - Express中要在
res.send()前调用res.setHeader('X-Frame-Options', 'DENY') - Next.js需在
next.config.js的headers()函数里按路由配置,不能靠前端JS补救
Content-Security-Policy frame-ancestors比X-Frame-Options更可靠
frame-ancestors是W3C标准,优先级高于X-Frame-Options;如果两者同时存在,Chrome等浏览器会直接忽略X-Frame-Options,造成策略冲突或维护混乱。
它支持多源白名单、'self'、'none',还能防御<object>、<embed>等其他嵌套方式,而X-Frame-Options只管<iframe>。
立即学习“前端免费学习笔记(深入)”;
- 完全禁止:
Content-Security-Policy: frame-ancestors 'none'; - 仅同源:
Content-Security-Policy: frame-ancestors 'self'; - 允许多个可信域名:
Content-Security-Policy: frame-ancestors 'self' https://admin.example.com https://partner.co; -
'none'和'self'必须加单引号,这是CSP语法硬性要求
前端JS检测top !== self为什么危险且无效
这段代码常见但实际起不到防护作用:if (top !== self) top.location = self.location;。现代浏览器对跨源top.location赋值会抛出SecurityError,而不是静默跳转;攻击者只要把恶意页设为同源(比如利用子域名),判断就彻底失效。
更严重的是,它无法阻止<iframe>已加载完成后的点击劫持——用户看到的是你页面,但点击坐标早已被CSS偏移重定向,JS检测根本来不及干预。
- 若页面本身允许被嵌入(如设置了
frame-ancestors 'self' https://trusted.com),此逻辑反而破坏正常业务流程 - 某些场景下配合
document.getElementById('frame-guard').style.display = 'block'做视觉干扰可作辅助提示,但不是强制阻断 - 永远不要用它替代服务端响应头
如何快速验证自己网站是否可被点击劫持
不用装插件,本地建一个test.html,填入最小复现代码:
<button>假装这是个诱饵按钮</button> <iframe src="https://your-site.com/withdraw" style="opacity:0.01; position:absolute; top:0; left:0; width:100%; height:500px; z-index:2;"></iframe>
把https://your-site.com/withdraw换成你自己的敏感页面地址(如支付页),确保你已在目标站登录,然后用Chrome/Firefox打开这个test.html。
- 观察点不是“能不能显示”,而是“是否静默加载成功”:DevTools → Elements里搜
iframe看是否挂载,Network里查是否发起了请求 - 如果能加载且无跳转、无报错、无拦截,说明当前页面仍可被嵌套,存在风险
- 测试必须已登录,否则无法触发真实操作场景
真正起作用的永远是服务端下发的frame-ancestors或X-Frame-Options;HTML结构和JS只能做有限辅助,别指望它们兜底。



















