首页 / 帮助文档 / 网站漏洞防护之点击劫持X-Frame-Options

网站漏洞防护之点击劫持X-Frame-Options

点击劫持(Clickjacking)是一种视觉欺骗攻击,攻击者将目标网站嵌入到一个透明的iframe层中,覆盖在看似正常的页面上。用户以为自己在点击游戏按钮或领取奖励,实际上点击的是隐藏在下面的“删除账号”、“转账汇款”或“授权登录”按钮。这种攻击之所以危险,是因为它不依赖任何脚本漏洞,纯粹利用HTML和CSS的特性,绕过了大多数CSRF令牌防护。

修复这个漏洞的核心手段就是在HTTP响应头中设置X-Frame-Options。这个响应头告诉浏览器,当前页面是否允许被其他页面通过iframe、frame、embed或object标签嵌入。它的值只有三个:DENY表示完全禁止嵌入;SAMEORIGIN表示只允许同源页面嵌入;ALLOW-FROM uri表示只允许指定来源嵌入。现代浏览器对ALLOW-FROM的支持已经过时,现在更推荐使用Content-Security-Policy的frame-ancestors指令来替代它。

X-Frame-Options的三个值如何选择

DENY是最严格的策略。如果你的网站没有任何页面需要被其他站点嵌入,直接设置成DENY。银行系统、支付页面、后台管理界面都应该使用这个值。它的响应头写法非常简单:

X-Frame-Options: DENY

SAMEORIGIN适用于需要在自身域名下使用iframe的场景。比如你的主站需要嵌入自己域名的客服聊天页面、数据报表或者上传组件。设置后,只有同协议、同域名、同端口的页面才能嵌入。注意,子域名也算不同源,www.example.com和api.example.com不能互相嵌入。配置方式:

X-Frame-Options: SAMEORIGIN

ALLOW-FROM理论上可以指定白名单域名,但Chrome和Safari早已不再支持这个值。如果你的业务确实需要允许特定第三方域名嵌入,必须使用CSP的frame-ancestors指令作为替代方案。

Content-Security-Policy的frame-ancestors才是未来

X-Frame-Options功能单一,只能控制能否嵌入,无法精细化管理。CSP的frame-ancestors指令可以指定多个允许的来源,支持通配符,还能配合其他CSP指令形成完整的安全策略体系。一个典型的配置如下:

Content-Security-Policy: frame-ancestors 'self' https://trusted-partner.com

这条规则表示允许同源页面和trusted-partner.com这个特定域名嵌入。如果想让任何页面都不能嵌入,直接设置成:

Content-Security-Policy: frame-ancestors 'none'

需要注意的是,如果同时设置了X-Frame-Options和frame-ancestors,浏览器会优先遵守frame-ancestors的规则。但为了兼容老版本浏览器,建议两个响应头都设置,保持双重防护。

不同Web服务器的具体配置方法

Nginx是目前使用最广泛的Web服务器,配置起来也最直观。在server块或者location块中添加add_header指令即可。全局禁止嵌入的配置:

server {
    add_header X-Frame-Options "DENY";
    add_header Content-Security-Policy "frame-ancestors 'none'";
}

如果只针对特定路径设置,比如只保护后台管理目录:

location /admin/ {
    add_header X-Frame-Options "DENY";
    add_header Content-Security-Policy "frame-ancestors 'none'";
}

Apache服务器需要确保mod_headers模块已启用,然后在配置文件或.htaccess中添加:

Header always set X-Frame-Options "DENY"
Header always set Content-Security-Policy "frame-ancestors 'none'"

这里的always参数很关键,它确保即使返回错误状态码(如404、500),响应头也会被添加。攻击者有时会故意触发错误页面来绕过安全头检查。

IIS服务器可以通过Web.config文件配置,在system.webServer节点下添加httpProtocol配置:


    
        
            
            
        
    

对于使用CDN或反向代理的场景,需要在代理层也配置这些响应头。很多CDN服务商提供了自定义响应头的功能,在控制台直接添加即可。如果CDN没有转发源站的响应头,防护就会失效。

前端框架中的补充防护手段

除了服务器端设置响应头,前端也可以使用JavaScript进行framebusting作为纵深防御。但要注意,现代浏览器中如果设置了X-Frame-Options或frame-ancestors,页面根本不会在iframe中加载,JavaScript也就无法执行。所以前端方案只能作为不支持响应头时的降级方案。

一个经典的framebusting脚本:

if (window.top !== window.self) {
    window.top.location = window.self.location;
}

这段代码检测当前窗口是否被嵌入,如果是就跳出iframe。但攻击者可以通过设置iframe的sandbox属性来阻止跳转,所以这个方法的可靠性有限。更完善的版本会结合样式隐藏:

style type="text/css">
    html { display: none; }

这段代码先隐藏整个页面,只有确认没有被嵌入时才显示。如果被嵌入就尝试跳出。但再次强调,服务器端响应头才是根本解决方案,前端代码只是辅助。

常见配置错误和排查方法

第一个常见错误是响应头重复设置。Nginx中如果有多个add_header指令,后面的会覆盖前面的。如果需要在同一个location中设置多个响应头,必须写在一起,或者使用ngx_headers_more模块。标准做法是把所有安全相关的响应头集中配置在一个文件中,然后通过include引入。

第二个错误是只设置了X-Frame-Options,没有设置frame-ancestors。前面提到过,ALLOW-FROM在主流浏览器中已经失效,如果你的业务需要白名单机制,必须用frame-ancestors。很多安全扫描工具会同时检查这两个响应头,缺少任何一个都可能被标记为风险。

第三个错误是配置了响应头但没有覆盖所有子路径。比如只在首页设置了,但其他页面没有。攻击者可以诱导用户点击嵌入了其他页面的陷阱。正确的做法是在Web服务器的全局配置中设置,然后对确实需要被嵌入的特定路径单独放宽策略。

排查方法很简单,打开浏览器的开发者工具,切换到Network标签,刷新页面后点击任意请求,在Response Headers区域查看是否有X-Frame-Options和Content-Security-Policy字段。也可以用curl命令快速检测:

curl -I https://your-domain.com

响应中应该能看到设置的安全头。如果看不到,检查Web服务器配置是否生效,是否有中间代理剥离了响应头。

点击劫持的变种攻击与防护盲区

除了传统的iframe透明层攻击,还有几种变种需要关注。拖放劫持(Drag and Drop Hijacking)利用HTML5的拖放API,诱导用户将敏感内容拖入攻击者控制的区域。这种攻击不完全依赖iframe,X-Frame-Options无法直接防护,需要配合CSP的sandbox指令限制拖放行为。

按键劫持(Strokejacking)通过iframe嵌入目标页面,攻击者捕获用户的键盘输入。虽然X-Frame-Options阻止了页面嵌入,但如果攻击者使用浏览器扩展或者恶意软件,仍然可能绕过。这类威胁需要端点安全方案来应对。

还有一个容易被忽略的盲区是移动端的WebView。某些App内嵌的WebView可能不遵守X-Frame-Options响应头,或者App开发者为了功能便利故意忽略安全策略。如果你的网站经常被嵌入到App中,需要与应用开发方确认WebView的安全配置,并要求启用对X-Frame-Options和CSP的支持。

安全头体系的整体规划

X-Frame-Options不应该孤立存在,它是一整套HTTP安全响应头的一部分。一个完善的安全头配置至少还应该包括:Strict-Transport-Security强制HTTPS连接;X-Content-Type-Options防止MIME类型嗅探;Referrer-Policy控制引用信息泄露;Permissions-Policy限制浏览器特性使用。这些响应头相互配合,才能构建纵深防御体系。

推荐的安全头配置模板:

Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Content-Security-Policy: frame-ancestors 'none'; default-src 'self'

这套配置禁止了页面被嵌入、禁止了MIME嗅探、限制了引用信息、关闭了摄像头麦克风和定位权限,同时建立了基础的CSP策略。根据业务需求调整具体值,但核心思路是默认拒绝一切,按需开放。

定期使用安全扫描工具检测响应头配置是否完整,关注CSP策略是否因为业务变更而出现漏洞。安全配置不是一劳永逸的事情,每次上线新功能、接入新第三方服务时,都需要重新评估是否需要调整安全策略。把安全头检查纳入CI/CD流程,在代码部署前自动验证,可以有效防止配置漂移导致的安全风险。