# Web 前端攻防（2014版）

- URL: https://wuming.si/blog/758/
- Author: Aaron
- Published: 2014-08-27
- Language: zh-Hans
- Categories: Uncategorized
- Originally published at: https://blog.wo.ai/archives/758

**禁止一切外链资源**

外链会产生站外请求，因此可以被利用实施 CSRF 攻击。

目前国内有大量路由器存在 CSRF 漏洞，其中相当部分用户使用默认的管理账号。通过外链图片，即可发起对路由器 DNS 配置的修改，这将成为国内互联网最大的安全隐患。

**案例演示**

百度旅游在富文本过滤时，未考虑标签的 style 属性，导致允许用户自定义的 CSS。因此可以插入站外资源：

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/eeebe74096_63918611gw1ejrccuj8h2j20mn0j6jtb.jpg)

所有浏览该页面的用户，都能发起任意 URL 的请求：

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/b547c157ab_63918611gw1ejrccvew0jj20mn0hujto.jpg)

由于站外服务器完全不受控制，攻击者可以控制返回内容：

  * 如果检测到是管理员，或者外链检查服务器，可以返回正常图片；
  * 如果是普通用户，可以返回 302 重定向到其他 URL，发起 CSRF 攻击。例如修改路由器 DNS：

    
    
    http://admin:admin@192.168.1.1/userRpm/PPPoECfgAdvRpm.htm?wan=0&lcpMru=1480&ServiceName=&AcName=&EchoReq=0&manual=2&dnsserver=黑客服务器&dnsserver2=4.4.4.4&downBandwidth=0&upBandwidth=0&Save=%B1%A3+%B4%E6&Advanced=Advanced

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/3415363ce4_63918611gw1ejrccwbk1aj20n60hujub.jpg)

演示中，随机测试了几个帖子，在两天时间里收到图片请求 500 多次，已有近 10 个不同的 IP 开始向我们发起 DNS 查询。

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/36ebbc2979_63918611gw1ejrccx7awmj20i102n74t.jpg)

通过中间人代理，用户的所有隐私都能被捕捉到。还有更严重的后果，查考[流量劫持危害探讨](http://fex.baidu.com/blog/2014/04/traffic-hijack-2/)

要是在热帖里『火前留名』，那么数量远不止这些。

如果使用发帖脚本批量回复，将有数以万计的用户网络被劫持。

**防范措施**

杜绝用户的一切外链资源。需要站外图片，可以抓回后保存在站内服务器里。

对于富文本内容，使用白名单策略，只允许特定的 CSS 属性。

尽可能开启 Content Security Policy 配置，让浏览器底层来实现站外资源的拦截。

**富文本前端扫描**

富文本是 XSS 的重灾区。

富文本的实质是一段 HTML 字符。由于历史原因，HTML 兼容众多不规范的用法，导致过滤起来较复杂。几乎所有使用富文本的产品，都曾出现过 XSS 注入。

**案例演示**

旅游发帖支持富文本，我们继续刚才的演示。

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/37c0deccf9_63918611gw1ejrccyqtvrj20lx0bnwft.jpg)

由于之前已修复过几次，目前只能注入 embed 标签和 src 属性。

但即使这样，仍然可以嵌入一个框架页面：

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/ec00fed0b2_63918611gw1ejrcczcgtcj20m10bswfq.jpg)

因为是非同源执行的 XSS，所以无法获取主页面的信息。但是可以修改 top.location，将页面跳转到第三方站点。

将原页面嵌入到全屏的 iframe 里，伪造出相同的界面。然后通过浮层登录框，进行钓鱼。

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/061d9806e2_63918611gw1ejrcd08qisj20nv0gwdi2.jpg)

总之，富文本中出现可执行的元素，页面安全性就大打折扣了。

**防范措施**

这里不考虑后端的过滤方法，讲解使用前端预防方案：

无论攻击者使用各种取巧的手段，绕过后端过滤，但这些 HTML 字符最终都要在前端构造成元素，并渲染出来。

因此可以在 DOM 构造之后、渲染之前，对离屏的元素进行风险扫描。将可执行的元素（script，iframe，frame，object，embed，applet）从缓存中移除。

或者给存在风险的元素，加上沙箱隔离属性。

  * 例如 iframe 加上  _sandbox_ 属性，即可只显示框架内容而不运行脚本
  * 例如 Flash 加上  _allowScriptAccess_ 及  _allowNetworking_ ，也能起到一定的隔离作用。

DOM 仅仅被构造是不会执行的，只有添加到主节点被渲染时才会执行。所以这个过程中间，可以实施安全扫描。

实现细节可以参考：[http://www.etherdream.com/FunnyScript/richtext _safe_ render.html](http://www.etherdream.com/FunnyScript/richtext_safe_render.html)

如果富文本是直接输入到静态页面里的，可以考虑使用 MutationEvent 进行防御。详细参考：<http://fex.baidu.com/blog/2014/06/xss-frontend-firewall-2/>

但推荐使用动态方式进行渲染，可扩展性更强，并且性能消耗最小。

**跳转 opener 钓鱼**

浏览器提供了一个 opener 属性，供弹出的窗口访问来源页。但该规范设计的并不合理，导致通过超链接打开的页面，也能使用 opener。

因此，用户点了网站里的超链接，导致原页面被打开的第三方页面控制。

虽然两者受到同源策略的限制，第三方无法访问原页面内容，但可以跳转原页面。

由于用户的焦点在新打开的页面上，所以原页面被悄悄跳转，用户难以觉察到。当用户切回原页面时，其实已经在另一个钓鱼网站上了。

**案例演示**

百度贴吧目前使用的超链接，就是在新窗口中弹出，因此同样存在该缺陷。

攻击者发一个吸引用户的帖子。当用户进来时，引诱他们点击超链接。

通常故意放少部分的图片，或者是不会动的动画，先让用户预览一下。要是用户想看完整的，就得点下面的超链接：

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/c3ec371733_63918611gw1ejrcd0nbddj20ke0fkq4p.jpg)

由于扩展名是 gif 等图片格式，大多用户就毫无顾虑的点了。

事实上，真正的类型是由服务器返回的 MIME 决定的。所以这个站外资源完全有可能是一个网页：

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/6176776d0a_63918611gw1ejrcd1tt3ej20ke0fk0uo.jpg)

当用户停留在新页面里看动画时，隐匿其中的脚本已悄悄跳转原页面了。

用户切回原页面时，其实已在一个钓鱼网站上：

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/6a128899b8_63918611gw1ejrcd2i0odj20ke0fkwgc.jpg)

在此之上，加些浮层登录框等特效，很有可能钓到用户的一些账号信息。

**防范措施**

该缺陷是因为 opener 这个属性引起的，所以得屏蔽掉新页面的这个属性。

但通过超链接打开的网页，无法被脚本访问到。只有通过 window.open 弹出的窗口，才能获得其对象。

所以，对页面中的用户发布的超链接，监听其点击事件，阻止默认的弹窗行为，而是用 window.open 代替，并将返回窗体的 opener 设置为 null，即可避免第三方页面篡改了。

详细实现参考：<http://www.etherdream.com/FunnyScript/opener_protect.html>

当然，实现中无需上述 Demo 那样复杂。根据实际产品线，只要监听用户区域的超链接就可以。

**用户内容权限**

支持自定义装扮的场合，往往是钓鱼的高发区。

一些别有用心者，利用装扮来模仿系统界面，引诱用户上钩。

**案例演示 – 空间越界**

百度空间允许用户撰写自定格式的内容。

其本质是一个富文本编辑器，这里不演示 XSS 漏洞，而是利用样式装扮，伪装一个钓鱼界面。

百度空间富文本过滤元素、部分属性及 CSS 样式，但未对 class 属性启用白名单，因此可以将页面上所有的 CSS 类样式，应用到自己的内容上来。

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/84422ae900_63918611gw1ejrcd33ecsj20i30art9k.jpg)

**防范措施**

规定用户内容尺寸限制，可以在提交时由用户自己确定。

不应该为用户的内容分配无限的尺寸空间，以免恶意用户设置超大字体，破坏整个页面的浏览。

最好将用户自定义的内容嵌套在 iframe 里，以免影响到页面其他部位。

如果必须在同页面，应将用户内容所在的容器，设置超过部分不可见。以免因不可预测的 BUG，导致用户能将内容越界到产品界面上。

**案例演示 – 功能越界**

自定义装扮通常支持站外超链接。

相比贴吧这类简单纯文字，富文本可以将超链接设置在其他元素上，例如图片。

因此这类链接非常具有迷惑性，用户不经意间就点击到。很容易触发之前提到的修改 opener 钓鱼。

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/cffabf56fa_63918611gw1ejrcd3yyghj20na0f7abp.jpg)

如果在图片内容上进行伪装，更容易让用户触发一些危险操作。

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/4447e97c60_63918611gw1ejrcd4otgoj20nd0famyd.jpg)

要是和之前的区域越界配合使用，迷惑性则更强：

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/0185a0777d_63918611gw1ejrcd5bqskj20n80f7dhl.jpg)

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/b2b036e069_63918611gw1ejrcd64dbfj20n50f8wfl.jpg)

**防范措施**

和之前一样，对于用户提供的超链接，在点击时进行扫描。如果是站外地址，则通过后台跳转进入，以便后端对 URL 进行安全性扫描。

如果服务器检测到是一个恶意网站，或者目标资源是可执行文件，应给予用户强烈的警告，告知其风险。

**点击劫持检测**

点击劫持算是比较老的攻击方式了，基本原理大家也都听说过。就是在用户不知情的前提下，点击隐藏框架页面里的按钮，触发一些重要操作。

但目前在点击劫持上做防御的并不多，包括百度绝大多数产品线目前都未考虑。

**案例演示**

能直接通过点击完成的操作，比较有意义的就是关注某用户。例如百度贴吧加关注的按钮：

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/8ecab619ad_63918611gw1ejrcd6r69aj20gr0ajgmp.jpg)

攻击者事先算出目标按钮的尺寸和坐标，将页面嵌套在自己框架里，并设置框架的偏移，最终只显示按钮：

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/e4b185c307_63918611gw1ejrcd74odjj2075051dfs.jpg)

接着通过 CSS 样式，将目标按钮放大，占据整个页面空间，并设置全透明。

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/e1782cf125_63918611gw1ejrcd85l6ej20jw0grgnf.jpg)

这时虽看不到按钮，但点击页面任意位置，都能触发框架页中加关注按钮的点击：

![](https://blog.wo.ai/wp-content/plugins/wp-o-matic/cache/ed980a6136_63918611gw1ejrcd8g5fqj20gr0ajmy8.jpg)

**防范措施**

事实上，点击劫持是很好防御的。

因为自身页面被嵌套在第三方页面里，只需判断 self == top 即可获知是否被嵌套。

对一些重要的操作，例如加关注、删帖等，应先验证是否被嵌套。如果处于第三方页面的框架里，应弹出确认框提醒用户。

确认框的坐标位置最好有一定的随机偏移，从而使攻击者构造的点击区域失效。

[Web 前端攻防（2014版）](http://blog.jobbole.com/76068/)，首发于[博客 – 伯乐在线](http://blog.jobbole.com)。
