点击劫持是什么?如何防范?
简化版
点击劫持是攻击者把目标页面嵌入透明或伪装的 iframe 中,诱导用户点击看似无害的位置,实际触发目标页面操作。防护方式包括设置 X-Frame-Options 或 CSP 的 frame-ancestors,限制页面被其他站点嵌入。
详细版
点击劫持常见过程:
- 攻击者创建一个诱导页面。
- 把目标站点放进 iframe。
- 用透明度、层级、样式伪装按钮位置。
- 用户点击诱导按钮,实际点到了 iframe 中的敏感操作。
防护方式:
X-Frame-Options: DENY:禁止被 iframe 嵌入。X-Frame-Options: SAMEORIGIN:只允许同源嵌入。- CSP
frame-ancestors 'self':更现代、更灵活。
前端 JS 判断 top/frame 不是可靠防护,核心应在响应头上限制。
完整版教学
一、点击劫持的本质
点击劫持攻击的关键不是偷密码,而是欺骗用户的点击。用户以为自己点的是抽奖、播放按钮,实际触发的是另一个网站里的确认、授权或支付操作。
它利用的是 iframe 嵌入能力和用户视觉误导。
二、为什么响应头有效
浏览器在加载 iframe 时会检查目标页面响应头。如果页面声明不允许被嵌入,浏览器会拒绝展示。
这比页面加载后再用 JS 判断更可靠,因为 JS 可能被禁用、执行太晚,或者被复杂场景绕过。
三、X-Frame-Options 和 frame-ancestors
X-Frame-Options 比较简单,常见值是 DENY 和 SAMEORIGIN。
CSP 的 frame-ancestors 更灵活,可以指定多个允许嵌入的来源。现代项目更推荐使用 CSP,但兼容性和历史系统也可能同时配置两者。
四、面试追问与工程落地
面试官可能问:“如果业务需要被合作方 iframe 嵌入怎么办?”
不能简单 DENY,而应明确白名单来源。例如只允许指定合作域名嵌入,并对嵌入场景做权限控制、消息通信校验和敏感操作二次确认。
工程中管理后台、支付页、授权页等敏感页面尤其应该禁止随意嵌入。
五、把浏览器判定流程讲清楚
frame-ancestors 是导航指令,浏览器会检查嵌套链上的每一层祖先;任何一层不匹配,最内层页面都不能加载。它不继承 default-src,所以只写 default-src 'none' 并不能禁止页面被嵌入。
Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENY
| 需求 | 推荐配置 | 说明 |
|---|---|---|
| 完全禁止嵌入 | frame-ancestors 'none' | 支付、授权、管理页面常用 |
| 仅允许同源 | frame-ancestors 'self' | 站内 iframe 场景 |
| 允许合作方 | 'self' https://partner.example | 必须精确维护来源 |
X-Frame-Options: ALLOW-FROM 支持不一致,不应作为现代白名单方案。兼容旧浏览器时可同时下发 XFO 与 CSP,但要让两者表达一致策略。
记忆钩子:
frame-src管“我能加载谁”,frame-ancestors管“谁能加载我”;点击劫持防护看后者。
六、用攻击算例和测试闭环验证
假设攻击页把银行确认按钮所在 iframe 设置为 opacity: 0.01,并将其定位到宽 160px、高 48px 的“领取奖励”按钮上。用户看到的是奖励按钮,命中的却是 iframe 中同一坐标的确认操作;攻击者不需要读取跨源 DOM,也能利用用户点击。
工程防护不能只配响应头,还要处理业务风险:敏感操作重新认证、展示不可伪造的交易摘要、限制一次性授权,能降低一次误点的损失。合作方嵌入时,postMessage 必须校验精确 origin,发送时也不要无脑使用 *。
上线后至少执行三组测试:恶意域名嵌入应失败;允许的合作域名应成功;合作域名再套一层未授权祖先时也应失败。最后在真实响应上检查头,不能只看应用配置文件,因为 CDN 或网关可能覆盖响应头。
恶意顶层页 → 合作方中间页 → 目标敏感页
未授权祖先存在 → 浏览器拒绝目标页
浏览器自动化还应验证响应经过反向代理后的最终结果,并把安全头纳入回归测试。若某条新路由漏掉中间件,测试应在发布前直接失败,而不是等人工发现。
监控侧还应采集 CSP 违规报告,但报告只用于发现异常来源,不能替代浏览器实际执行的阻断策略。
七、常见误区与追问
- 误区:同源策略会自动阻止点击劫持。 攻击者不需要读取 iframe 内容,只需要诱导用户在正确坐标点击。
- 误区:
default-src 'none'会自动禁止被 iframe 嵌入。frame-ancestors没有default-src回退,必须显式配置。 - 误区:前端检测
top !== self就足够。 脚本执行太晚且可能受沙箱和策略影响,响应头才是主防线。 - 追问:
frame-src与frame-ancestors有什么区别? 前者限制当前页面加载哪些 frame,后者限制哪些祖先可嵌入当前页面。 - 追问:为什么合作方白名单仍要校验 postMessage? 能嵌入页面不代表其发送的每条消息可信,消息来源和数据结构仍需验证。
- 追问:按钮加验证码能替代响应头吗? 不能;它只提高部分操作成本,页面仍暴露在视觉覆盖攻击面中。
- 追问:怎样验证 CDN 没有丢掉安全头? 对最终线上 URL 发请求并在多个节点检查响应头,再用真实 iframe 做正反用例。
八、加强记忆
- 攻击条件:目标可被嵌入,攻击者能对齐透明层,用户已有操作权限。
- 主防线:现代浏览器使用 CSP
frame-ancestors,旧兼容可补 XFO。 - 方向区别:
frame-src管向外加载,frame-ancestors管被谁加载。 - 白名单:业务确需嵌入时只放精确来源,并检查整条祖先链。
- 纵深防御:敏感操作重新认证、明确摘要,降低误点后果。
- 验证方式:线上响应头加恶意、合法、嵌套三类 iframe 用例一起测。