|
三年前我帮一家电商公司做安全测试,点击他们的劫持后台登录页有个“记住密码”的勾选框。我随手在测试页里嵌了个透明iframe,漏洞把那个勾选框对准了用户鼠标点击的修复位置——结果你猜怎么着?超过六成的测试用户都“主动”帮我勾上了这个选项。更绝的点击是,我还能把iframe里的劫持按钮替换成“确认支付”,只要用户在我伪造的漏洞页面上点任何地方,钱就出去了。修复这就是点击点击劫持,一种不需要任何漏洞就能让用户“自愿”交出权限的劫持攻击方式。 原理其实特别简单。漏洞攻击者建一个恶意网页,修复在上面放一个透明的点击iframe,iframe里加载的劫持是受害网站(比如你的银行、你的漏洞后台管理系统)。通过CSS把iframe的透明度设成0,再用position定位把它精确覆盖在恶意页面的按钮或链接上。用户看到的是一张美女图片或者“点击领取红包”的按钮,实际点下去却是在操作iframe里的目标网站。整个过程中,用户的浏览器完全正常,没有弹窗,没有跳转,连地址栏都是正常的——这就是它最阴险的地方。 修复方案其实不复杂,但很多人搞错重点。最有效的招是给页面响应头加上X-Frame-Options: DENY,这是老牌方案,所有主流浏览器都认。DENY表示任何页面都不能用iframe加载我这个页面,SAMEORIGIN则表示只有同域名才能嵌。但注意,这个头有个缺陷——它只能管住页面级的嵌入,如果你有个别接口需要被第三方合法调用(比如支付回调),那就得用更精细的Content-Security-Policy: frame-ancestors 'self'。这个指令能指定允许哪些域名来嵌你的页面,比X-Frame-Options灵活得多。 但光靠响应头还不够。有些老浏览器(比如IE8以下)不认这些头,或者用户用了某些奇葩的浏览器插件把响应头给剥了。这时候就得靠JavaScript的防御——在页面加载时判断window.self === window.top,如果发现自己被嵌在iframe里,直接跳转或者显示警告。不过这个方法有个坑:攻击者可以在iframe的sandbox属性里加上allow-scripts但去掉allow-top-navigation,这样你的JS虽然能执行,但没法跳转。所以最稳妥的做法是双重防护——服务端设响应头,客户端再做一次frame busting,而且客户端代码别用top.location跳转,用window.open或者直接改location.href来绕过沙箱限制。 我见过最蠢的修复方式是只在前端加个onclick事件检测——判断点击坐标是否在iframe范围内。这玩意儿纯属自欺欺人,攻击者只要把iframe的滚动条或者尺寸微调一下,坐标就对不上了。还有个真实案例,某银行APP的H5页面只加了X-Frame-Options: SAMEORIGIN,结果攻击者用子域名(比如evil.bank.com)照样能嵌进去——因为SAMEORIGIN只认域名后缀,不认子域。最后他们改成DENY才堵上。 别迷信那些所谓的“点击劫持测试工具”,它们只能扫出最基础的漏洞。真正的风险往往出在你意想不到的地方——比如你自己写的内部管理系统,觉得没人会攻击它,结果被钓鱼邮件诱导去点了个恶意链接。我建议每个开发者在发布任何页面之前,都手动打开开发者工具,在Console里跑一句window.self !== window.top看看有没有被嵌。这比任何自动化工具都靠谱。 说到底,点击劫持是个“信任劫持”——用户信任了错误的页面,而你的网站成了帮凶。修复它不需要多高深的技术,但需要你认真对待每一个对外发布的页面。别等出了事再补,那时候用户已经把钱转走了。 |