最近在把思享汇页面通过 <iframe>
标签嵌到 OA 系统中的时候遇到了些问题,在网站找了些资料和自己倒腾,总算是解决了。
介绍
X-Frame-Options
是一个符合 RFC 7034 标准的响应头,用来指示浏览器是否允许一个页面在 <frame>
, <iframe>
, <embed>
或者 <object>
标签中展示响应内容。如果服务器响应头信息中没有 X-Frame-Options
,网站会存在点击劫持的风险。
下面用一个例子说明下点击劫持,示例源自 CSDN。
攻击者的通常做法,是在自己的页面里通过 <iframe>
的形式,包含一个不属于它本站的页面。下面的示例代码里包含的是 163 邮箱的设置页。而由黑客控制的父级页面本身可以是任何内容,它通过精确调整自己页面的内容和 <iframe>
的坐标及大小,再通过 CSS 的 opacity
透明度设置,把用户内容所在的<iframe>
透明度设置为全透明。
以下为示例代码:
<!DOCTYPE html> <html lang="en"> <head> <style> .iframe { opacity: 0.4; } body { background: url(./sales.png) no-repeat fixed top; } </style> </head> <body> <iframe src="https://m.reg.163.com/?email=1/#/email" width="100%" height="600px" frameBorder="0" class="iframe" scrolling="no" allowtransparency="true"> </iframe> </body> </html>
得到的效果如下图:

请注意为了展示效果,特意设置了透明度为 {opacity:0.4;}
,保持页面上能隐约看到 163 邮箱的内容。但如果 CSS 代码设置为 {opacity:0;}
,163 邮箱的内容就会消弭于眼前,只留下这张促销图片。但访问者点到相应位置时,依然会触发对 163 邮箱的请求,这个就是点击劫持的原理。
早期 WEB 开发者应对这个问题的处理,是用 JavaScript 实现的。一般是判断当前 window 对象和 parent 对象是否一致,如果不一致,就执行“破框”跳转。但这个方式并不可靠,因为各种原因,客户端有可能禁止了 JavaScript 执行,或者是代码被绕过,这样“破框”代码就失效了。
在认识到这种攻击方式的危害性后,为统一解决这个问题,制定互联网规范的机构出手了,解决方案就是 RFC 7034 标准的 X-Frame-Options
响应头。
语法
X-Frame-Options
有三个可能的值:
- X-Frame-Options: deny
- X-Frame-Options: sameorigin
- X-Frame-Options: allow-from https://example.com
三种值的含义是:
deny
:完全不能被嵌入到<iframe>
、<frame>
等标签中。sameorigin
:只能在相同域名页面的<iframe>
、<frame>
等标签中。allow-from uri
: 可以在指定来源的 uri 被嵌入到<iframe>
、<frame>
等标签中。
配置
我们使用的是 Nginx,其他主流服务器的配置也贴出来了。
Nginx
配置 Nginx 发送 X-Frame-Options
响应头,需要把下面这行添加到 http
, server
或者 location
的配置中:
add_header X-Frame-Options sameorigin always;
或者
add_header X-Frame-Options deny;
或者
add_header X-Frame-Options allowall;
或者
add_header X-Frame-Options allow-from https://example.com;
Apache
将 Apache
的 X-Frame-Options
配置成 sameorigin
,需要把下面这行添加到 site
的配置中:
Header always set X-Frame-Options “sameorigin”
将 Apache
的 X-Frame-Options
配置成 deny
:
Header always set X-Frame-Options “deny”
将 Apache
的 X-Frame-Options
配置成 allow-from
:
Header always set X-Frame-Options “allow-from https://example.com/”
IIS
配置 IIS
发送 X-Frame-Options
响应头,添加下面的配置到 Web.config
文件中:
<system.webServer> ... <httpProtocol> <customHeaders> <add name="X-Frame-Options" value="sameorigin" /> </customHeaders> </httpProtocol> ... </system.webServer>
HAProxy
配置 HAProxy
发送 X-Frame-Options
,添加这些到你的前端、监听 listen,或者后端的配置里面:
rspadd X-Frame-Options:\ sameorigin
或者,在更加新的版本中添加:
http-response set-header X-Frame-Options sameorigin
Express
要配置 Express
可以发送 X-Frame-Options
,你可以用借助了 frameguard
来设置头部的 helmet
。
在你的服务器配置里面添加:
const helmet = require('helmet');
const app = express();
app.use(helmet.frameguard({ action: "sameorigin" }));
或者,你也可以直接用 frameguard
:
const frameguard = require('frameguard')
app.use(frameguard({ action: 'sameorigin' }))
问题分析
我们碰到的问题是,为了防止点击劫持,运维在前置的 nginx 统一配置了 sameorigin
响应,使网站的页面不能被嵌入到其他网站。
修改以后在又因为整个请求链路上有多台 nginx,每台配置不一样导致 X-Frame-Options
返回了多个有冲突的值,还是没法嵌入。
最后修改成一致后,解决了这个问题。如图所示,多个同样的 allowall
就没问题。

发表回复