在阅读该文章之前,建议对 HTTP 有所了解,可以看HTTP 入门体检,会对以下的内容有所帮助。
X-Frame-Options 响应首部字段是用来告诉浏览器该网页是否能被 frame,iframe,embed,object 元素嵌入。X-Frame-Option 可以确保站点不被这些元素嵌入,免得遭受点击劫持的攻击。
在说 X-Frame-Options 之前,当然要先简单的介绍下那几个嵌入元素。
嵌入元素
frame
已废弃。因为存在一些性能问题,以及使用屏幕阅读器的用户缺少可访问性。
embed
将外部内容嵌入站点,比如插件,Flash <embed src=“test.swf” />、视频 <embed type=“video/quicktime” src=“movie.mov” width=“640” height=“480”>。
object
嵌入对象元素,表示引入一个外部资源,这个资源可能是一张图片,一个嵌入的浏览上下文,亦或是一个插件所使用的资源。
先在这里停住,乍看之下,object 和 embed 的作用是一模一样的,都是表示引入一个外部资源。但是实际上,可以说是相同作用的。但是, object 标签只支持 IE 系列的浏览器或者其他支持 ActiveX 控件的浏览器,而 embed 则可以被大多数浏览器识别。因此在嵌入 flash 时,为了让大多数浏览器能够正常显示 flash,需要把 embed 标签嵌套放在 object 标签内。
简单来说,就是 IE 可以识别 embed,但是为了让其他浏览器也能识别,建议加上 object
1 | <object width="200" height="200" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"> |
iframe
iframe 可以讲一大篇文章,但是不配合着讲 iframe 的来龙去脉,对X-Frame-Option 的理解可能会打折扣。iframe 出生在乱世(插件技术,也就是Flash、Java Applet),现在也不建议使用,因为存在安全隐患。为什么会存在安全隐患呢?因为在没有加任何限制的情况下,iframe 嵌入的网站是可以被修改的。
1 | // html |

因此可见,假如在我们没有做任何的防御措施的情况下,肯定是会存在安全问题,例如广告劫持,点击劫持等等。那就让我们来学习一下,遇到这类问题如何处理。
防范
sandbox
sandbox 是 iframe 的一个属性值,该属性对呈现在 iframe 框架中的内容启用一些额外的限制条件。属性值可以为空字符串(这种情况下会启用所有限制),也可以是用空格分隔的一系列指定的字符串。IE 9及其以下版本不支持。
| 值 | 描述 |
|---|---|
| allow-downloads-without-user-activation | 允许在没有征求用户同意的情况下下载文件 |
| allow-forms | 允许嵌入的浏览上下文提交表单。如果该关键字未使用,该操作将不可用 |
| allow-modals | 允许内嵌浏览环境打开模态窗口 |
| allow-orientation-lock | 允许内嵌浏览环境禁用屏幕朝向锁定(手机、平台垂直或水平转向) |
| allow-pointer-lock | 允许内嵌浏览环境使用鼠标锁定 |
| allow-popups | 允许弹窗 (类似window.open, target=”_blank”, showModalDialog),默认失效 |
| allow-popups-to-escape-sandbox | 允许沙箱文档打开新窗口,并且不强制要求新窗口设置沙箱标记。 例如,这将允许一个第三方的沙箱环境运行广告开启一个登陆页面,新页面不强制受到沙箱相关限制。 |
| allow-same-origin | 允许同源访问。 |
| allow-scripts | 允许嵌入的浏览上下文运行脚本(但不能创建弹窗)。 |
| allow-top-navigation | 允许嵌入的页面的上下文导航(加载)内容到顶级的浏览上下文环境(browsing context)。 |
| allow-top-navigation-by-user-activation | 允许嵌入的页面的上下文在经过用户允许后导航(加载)内容到顶级的浏览上下文环境 |
sandbox 的属性实在是太多了。但是我们用来防御 iframe 攻击的常用的属性有 allow-scripts/allow-forms/allow-same-origin,而其他的属性值视情况而定。1
<iframe sandbox=”allow-forms allow-same-origin allow-scripts” src="http://www.test.com" name="test"></iframe>
window.top.location
因为在上面说过 sandbox 有浏览器兼容的问题,但是有时候我们需要在低版本浏览器运行程序,而且往往低版本浏览器存在的问题会相对比较多。因此除了 sandbox 之外,我们还可以通过原生JavaScript 代码来防止被嵌套。
正常的网页window.top === window,但是一旦正常的网站被嵌套了之后,window 指向的是 iframe,也就是上面所说的 iwindow,而window.top 则是指嵌套网站的容器,因此我们可以利用window.top === window 来防止正常网站被嵌套。
1 | try { |
X-Frame-Options
这是我们本期的主角,但是它可以讲的内容其实并不多,只是因为归类在 HTTP 响应首部字段。它有三个值,DENY/SAMEORIGIN/ALLOW-FROM xxx。
DENY:禁止任何形式的嵌套;SAMEORIGIN:只允许嵌入页和被嵌入页在同一个域名下,即受同源策略所限制;ALLOW-FROM xxx:只允许指定的站点嵌入网页,如ALLOW-FROM http://test.com;
X-Frame-Options 只是把拦截 iframe 控制权交给了服务端,本质上跟我们之前通过 window.top.location 的效果一样的,它相当于 SAMEORIGIN,因此我们可以通过 JavaScript 也可以达到一样的作用。
1 | // DENY |
总结
iframe 虽然可以做很多事情,也因为历史的车轮逐渐慢慢淘汰,但是不代表点击劫持我们就可以无视,除了 HTTP X-FRAME-OPTIONS 之外,还有 CSP 可以帮助我们拦截点击劫持,这个我们下一次再说。总而言之,能不用iframe 就不用 iframe,同时也要考虑自己的站点允不允许被嵌入防止攻击。