天涯旅店

X-Frame-Options 的使用

最近在把思享汇页面通过 <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

ApacheX-Frame-Options 配置成 sameorigin,需要把下面这行添加到 site 的配置中:

Header always set X-Frame-Options “sameorigin”

ApacheX-Frame-Options 配置成 deny:

Header always set X-Frame-Options “deny”

ApacheX-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 就没问题。

结果

发表回复

textsms
account_circle
email

天涯旅店

X-Frame-Options 的使用
最近在把思享汇页面通过 <iframe> 标签嵌到 OA 系统中的时候遇到了些问题,在网站找了些资料和自己倒腾,总算是解决了。 介绍 X-Frame-Options 是一个符合 RFC 7034 标准…
扫描二维码继续阅读
2020-08-06