点击劫持-clickjacking
场景复现:小李登录某网站后,没过多久弹出一段“有趣的”链接,好奇心趋势下小李打开了链接...,跟着提示点来点去,最后啥都没看到,啥都没干成。郁闷之余关闭链接地址后惊奇的发现在已经登录的网站页面居然多了几行操作记录,且操作记录正是属于自己!心态 爆炸...
通过用户的点击完成了一系列操作,但是用户对此并不知晓发生了什么?
危害
- 冒用用户操作
- 获取用户敏感信息(邮件)
- 盗取用户资金(转账、消费)
特性:
- 用户自己操作
- 用户自己不知情
这种攻击利用HTML中的<iframe>标签的透明属性,就像在一幅画上铺了一层透明的纸,画就是攻击页面,透明的纸就是用户登录的网站页面。用户看到的是攻击页面,但其实这个页面之上是网站的页面,网站页面被透明化,攻击页面和网站页面的按钮位置、点击方式几乎都一模一样。这时用户看到的是攻击页面,用户以为自己操作的也是攻击页面,但是实际上操作的确实网站页面;如果这个网站是用户的邮箱、银行账户,并且你还噼里啪啦操作了一堆,心里顿时有点凉!
案例分析
登录某网站后,弹出了一段“点击和美女聊天的链接",当用户点击了“和美女聊天”的链接后,在已经登录的博客发表了一篇帖子...
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>clickhijack</title>
</head>
<!--background是本地的一张有趣的图片 -->
<body style="background:url(clickjacking.jpeg) no-repeat;background-size: cover;">
<!-- src是笔者本地启动的nodejs项目添加评论页面-->
<iframe style="opacity:0.3" src="http://localhost:3000/post/1" width="1260" height="600"></iframe>
</body>
</html>
页面显示的效果是这样:
页面真实的效果是这样:
这里将iframe的透明度改成了0.5
当然真实的攻击页面内容会很丰富很吸引人。笔者这里只做演示,攻击思路是一样的。
攻击防御
通过案例分析得知,点击劫持的产生是基于iframe发生的,我们防御的思路也应该是基于此。
JavaScript禁止内嵌
HTML DOM top 属性
定义和用法
Top属性返回当前窗口的最顶层浏览器窗口。
该属性返回对一个顶级窗口的只读引用。如果窗口本身就是一个顶级窗口,top 属性存放对窗口自身的引用。如果窗口是一个框架,那么 top 属性引用包含框架的顶层窗口。
实例
下面的例子窗口是否在一个框架中,如果是,则跳出框架;
<!--下面案例来自W3c -->
<html>
<head>
<script type="text/javascript">
function breakout() {
if (window.top != window.self) {
window.top.location = "test.html"
}
}
</script>
</head>
<body>
<form>
Click the button to break out of the frame:
<input type="button" onclick="breakout()" value="Break out!">
</form>
</body>
</html>
了解top属性的同时,我们即可把top属性应用到点击劫持中。
代码实现:
正常的页面top属性和window属性对比:
top
>Window {window: Window, self: Window, document: document, name: "", location: Location, …}
top.location
>Location {ancestorOrigins: DOMStringList, href: "http://localhost:3000/post/1", origin: "http://localhost:3000", protocol: "http:", host: "localhost:3000", …}
window
>Window {window: Window, self: Window, document: document, name: "", location: Location, …}
top===window
>true
点击劫持的页面top属性和window属性对比:
top
>global {0: Window, window: global, self: global, location: {…}, closed: false, frames: global, …}
top.location
>{then: undefined, Symbol(Symbol.toStringTag): undefined, Symbol(Symbol.hasInstance): undefined, Symbol(Symbol.isConcatSpreadable): undefined, replace: ƒ}
window
>Window {window: Window, self: Window, document: document, name: "", location: Location, …}
top===window
//false
由以上区别做基础即可进行判断,示例代码如下:
<script type="text/javascript">
if (top.location != windowlocation)
{
top.location = window.location
}
</script>
到此似乎是解决了点击劫持的问题,但是真的100%解决了吗?答案肯定是没有。
只要能禁止执行脚本就会再次触发攻击!恰好iframe中的sandbox刚好有这作用.
HTML <iframe> 标签的 sandbox 属性
定义和用法
如果被规定为空字符串(sandbox=""),sandbox 属性将会启用一系列对行内框架中内容的额外限制。
sandbox 属性的值既可以是一个空字符串(应用所有的限制),也可以是空格分隔的预定义值列表(将移除特定的限制)。
语法
<iframe sandbox="value">
属性值
| 值 | 描述 |
|---|---|
| "" | 应用以下所有的限制。 |
| allow-same-origin | 允许 iframe 内容被视为与包含文档有相同的来源。 |
| allow-top-navigation | 允许 iframe 内容从包含文档导航(加载)内容。 |
| allow-forms | 允许表单提交。 |
| allow-scripts | 允许脚本执行。 |
了解sanbox属性后,我们即可将设置属性就能再次触发攻击!
示例:
<iframe style="opacity:0.3" src="http://localhost:3000/post/1" width="1260" height="600" sandbox="allow-forms"></iframe>
<!-- 允许表单提交事件,剩下的都会被禁止 -->
这时又可以开始点击劫持攻击了!
X-FRAME_OPTIONS禁止内嵌
The X-Frame-Options HTTP 响应头是用来给浏览器 指示允许一个页面 可否在 <frame>, <iframe>, <embed> 或者 <object> 中展现的标记。站点可以通过确保网站没有被嵌入到别人的站点里面,从而避免 clickjacking 攻击。
语法:
X-Frame-Options 有三个可能的值
X-Frame-Options: deny
X-Frame-Options: sameorigin
X-Frame-Options: allow-from https://example.com/
属性值
| 值 | 描述 |
|---|---|
| deny | 表示该页面不允许在frame中展示,及时是相同域名页面的嵌套 |
| sameorigin | 表示该页面可以在相同域名页面的 frame 中展示。 |
| allow-from uri | 表示该页面可以在指定来源的 frame 中展示。 |
实例:
Note: 设置 meta 标签是无效的!例如 没有任何效果。不要这样用!只有当像下面示例那样设置 HTTP 头 X-Frame-Options 才会生效。
配置 Apache
要将 Apache 的配置 X-Frame-Options 设置成 deny , 按如下配置去设置你的站点:
Header set X-Frame-Options "deny"
要将 Apache 的配置 X-Frame-Options 设置成 allow-from,在配置里添加:
Header set X-Frame-Options "allow-from example.com/"
配置 nginx
配置 nginx 发送 X-Frame-Options 响应头,把下面这行添加到 'http', 'server' 或者 'location' 的配置中:
add_header X-Frame-Options sameorigin always;
配置 NODEJS_KOA框架
示例代码:
//有可能被攻击的页面直接禁止被内嵌->DENY
router.all('/*', async function(ctx, next){
ctx.set('X-Frame-Options','DENY')
await next();
});
浏览器兼容性
X-FRAME_OPTIONS浏览器兼容性良好,读者可放心使用。
详细兼容性情况读者可参考文末X-FRAME_OPTIONS参考资料
其他辅助手段
- 添加校验码
- 添加校验短信 ...
参考资料
百度百科——点击劫持
W3school-top属性
W3school-<iframe>标签的sandbox属性
MDN-X-Frame-Options
结语
点击劫持对于WEB安全来讲,至关重要。尤其是对iframe嵌套项目来讲。
本文记录的是笔者在开发过程中遇到问题引发的思考和探索。可供有类似问题的读者参考。
其他安全方面的文章笔者会持续更新,欢迎各位读者提出意见和建议。