共计 2360 个字符,预计需要花费 6 分钟才能阅读完成。
为什么 CSRF Token 防不住点击劫持?
很多开发者认为有了 CSRF Token(跨站请求伪造令牌)就高枕无忧,但点击劫持 (Clickjacking) 却能轻松绕过这个防护。这是因为:

- CSRF Token 验证的是请求来源的合法性,而点击劫持攻击是让用户在不知情的情况下 ” 自愿 ” 触发操作
- 攻击者通过 iframe 嵌套目标页面并叠加诱导 UI(如透明按钮),用户实际点击的是攻击者设计的界面
典型攻击流程:
- 攻击页面通过 iframe 加载银行转账页面(该页面有合法的 CSRF Token)
- 用 CSS 将 iframe 透明度设为 0,并在关键按钮位置叠加攻击者设计的 ” 抽奖按钮 ”
- 用户点击 ” 立即抽奖 ” 时,实际触发的是银行页面的转账操作
三大防御方案对比
1. X-Frame-Options
最基础的防御手段,通过 HTTP 响应头控制页面能否被嵌入 iframe:
DENY:禁止任何框架嵌套SAMEORIGIN:只允许同源嵌套ALLOW-FROM uri:允许指定来源嵌套(已被现代浏览器废弃)
优点:
– 实现简单,兼容 IE8+
缺点:
– 无法精细控制嵌入行为
– 部分浏览器仍支持 ALLOW-FROM 但效果不一致
2. Content-Security-Policy (CSP)
更现代的解决方案,通过 frame-ancestors 指令控制:
Content-Security-Policy: frame-ancestors 'self' https://trusted.example.com
优点:
– 支持白名单配置
– 可与其他 CSP 策略组合使用
缺点:
– 需要处理浏览器兼容性问题(IE11 部分支持)
3. Frame Busting 脚本
传统的前端防御方案,通过 JS 尝试跳出 iframe:
if (top !== self) top.location = self.location;
优点:
– 可作为备用方案
缺点:
– 容易被绕过(sandbox 属性、X-Frame-Options 忽略等)
代码实现
Spring Boot 配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// 禁用默认的 X -Frame-Options(如果要用 CSP).headers().frameOptions().disable()
.and()
// 动态生成 nonce 值用于 CSP
.headers().contentSecurityPolicy("script-src'self''nonce-{nonce}'; frame-ancestors 'self'")
.and()
// 添加 X -Frame-Options 作为降级方案
.headers().frameOptions().sameOrigin();}
}
Django 配置
# settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
# ...
]
# 动态 CSP 中间件
CSP_FRAME_ANCESTORS = ["'self'"]
# 视图层添加 nonce
from django.utils.crypto import get_random_string
def my_view(request):
nonce = get_random_string(length=16)
request.content_security_policy = f"script-src'self''nonce-{nonce}'; frame-ancestors 'self'"
return render(request, 'template.html', {'nonce': nonce})
攻防验证
攻击模拟
-
创建攻击页面 attack.html:
<style> iframe { opacity: 0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; } button { position: absolute; top: 100px; left: 100px; } </style> <button> 点击抽奖 </button> <iframe src="https://victim.com/transfer"></iframe> -
在 Chrome DevTools 中观察:
- 网络请求会携带合法的 CSRF Token
- 用户界面完全被攻击者控制
防御生效验证
正确配置后:
- 浏览器控制台会显示拒绝加载 iframe 的警告
- 网络请求中能看到正确的安全头
- Frame Busting 脚本会在旧浏览器中生效
生产环境建议
SPA 应用特别注意
- Vue/React 等框架需要正确处理 nonce:
// 在 index.html 中 <script nonce="{{nonce}}"> window.__webpack_nonce__ = document.currentScript.nonce; </script>
Nginx 统一配置
add_header X-Frame-Options "SAMEORIGIN";
add_header Content-Security-Policy "frame-ancestors'self';";
进阶思考
- 如何防御通过 WebSocket 渠道实施的点击劫持?
- 在微服务架构下如何统一管理 CSP 策略?
- 当需要部分页面允许被第三方嵌入时(如 OAuth 授权),如何安全实现?
点击劫持防御需要纵深防御策略,没有任何单一方案能提供完美保护。建议组合使用 CSP+X-Frame-Options,并在关键操作添加二次确认机制。定期使用 OWASP ZAP 等工具进行渗透测试,确保防护持续有效。
正文完
