5

WEB安全防护相关响应头(上)

 2 years ago
source link: https://my.oschina.net/tcxa/blog/5068272
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

WEB 安全攻防是个庞大的话题,有各种不同角度的探讨和实践。即使只讨论防护的对象,也有诸多不同的方向,包括但不限于:WEB 服务器、数据库、业务逻辑、敏感数据等等。除了这些我们惯常关注的方面,WEB 安全还有一个重要的元素——网站的使用者

他们通常是完全没有 IT 知识的普通用户,网站方可以做点什么,以增加对这些普通用户的保护呢?以前较被忽略的步骤是:正确设置页面的响应头 (Response Headers) 。这类加入安全相关响应头的做法,往往是为了保护客户端/使用者的安全,减少使用者落入黑客的 WEB 陷阱的可能。

这里我们介绍一些较为常用的,和安全相关的响应头。当然,WEB 应用应该根据自己的实际情况部署和设置,并非盲目地一股脑地全部招呼上。

一、X-Frame-Options -- 打破框框

从 2008 年开始,研发人员发现一种利用视觉误导,引诱使用者行为的可能,这种做法后来被命名为点击劫持 (Click Jacking)

攻击者的通常做法是,在自己的页面里通过框架(iframe)的形式,包含一个不属于它本站的页面。下面的示例代码里包含的就是 【163 邮箱】的设置页。而由黑客控制的父级页面本身可以是任何内容,它通过精确调整自己页面的内容和 iframe 的坐标及大小,再通过 CSS 的 opacity 透明度设置,把用户内容所在的 iframe 透明度设置为全透明。

以下为示例代码:

<style>
   .iframe{opacity: 0.4;}
  body { background: url(./sales.png) no-repeat fixed top;
  }   
</style>


<iframe src="https://m.reg.163.com/?email=1/#/email" width="100%" height="600px" frameborder="0" class="iframe" scrolling="no" allowtransparency="true">
</iframe>

得到的效果如下图:

(点击查看大图)

请注意我们为了示例效果,特意设置了透明度仅仅为 {opacity:0.4;} ,保持页面上能隐约看到 163 邮箱的内容。但如果CSS代码设置为 {opacity:0;} ,163 邮箱的内容就会消弭于眼前,只留下这张促销图片。但访问者点到相应位置时,依然会触发对163邮箱的请求。这个就是点击劫持的原理。

早期 WEB 开发者应对这个问题的处理,是用 JavaScript 实现的,一般是判断当前 window 对象和 parent 对象是否一致,如果不一致,就执行“破框”跳转。但这个方式并不可靠!因为各种原因,客户端有可能禁止了 JavaScript 执行或代码被绕过,这样“破框”代码就失效了。在人们日益认识到这种攻击方式的危害性后,为统一解决这个问题,制定互联网规范的机构出手了,解决方案就是:

> RFC7034 > 「 HTTP Header Field X-Frame-Options 」 > https://tools.ietf.org/html/rfc7034

即引入了一个新的 HTTP 响应头。现在主流常用浏览器已全部支持这一响应头。具有这个响应头的资源,可以拒绝自己被非法站点的以下标签引用:

  • iFrame 标签
  • Frame 标签
  • Object 标签
  • Applet 标签
  • Embed 标签

这个头有三种选项,对应如下:

三种选项 具体含义 X-Frame-Options: DENY 完全不能被嵌入到 iframe、frame 等标签中 X-Frame-Options: SAMEORIGIN 只能被同源页面嵌入到 iframe 或者 frame 中 X-Frame-Options: ALLOW-FROM https://example.com/ 只能被指定的 URI 嵌入到 iframe 或 frame 中

所以显然,上面 163 邮箱的页面,就没有返回这个响应头,存在一定的劫持风险。如果加入这个响应头,我们的模拟页面,将无法像上图那样直接把 163 邮箱的内容嵌进来。

这个响应头的 弊端

  • 某些早期浏览器可能不支持;
  • 对确实需要嵌入很多第三方资源的复杂页面不适用。

二、X-Content-Type-Options -- IE你别瞎猜猜了

WEB 服务器返回的资源包括各种,如图片、HTML 页面、JavaScript 脚本、CSS 脚本、纯文本、二进制文件等。浏览器对资源的解读和渲染呈现方式,渗透攻击时有可能被利用。比如一个允许交互的站点,往往允许上传图片、mp3 文件,甚至允许上传纯文本文件,但往往不允许上传 JavaScript 脚本文件和 HTML 文件,因为后者借助 JavaScript 日益强大的功能,能做的坏事实在有点多。

如无意外,服务器端返回的每个资源的响应头里,一般都带有 content-type 这个响应头:

HTTP/1.1 200 OK
Content-Length: 3587
Content-Type: image/png
Date: Wed, 20 Mar 2019 09:40:16 GMT
Last-Modified: Tue, 19 Mar 2019 20:01:14 GMT
Server: Microsoft-IIS/7.5

返回的这个 Content-Type 头,一般是自动的,如服务器会根据 URL 后缀对应的文件类型自动选择;如不是自动的,则可能是程序员在代码层设定的,两种情况均有可能。这个响应头在较为罕见的情况下,也可能缺失,也可能和实际情况不匹配。而比较早期的浏览器,尤其是 IE,会出于“好心”,不但在没有 Content-Type 头的时候会主动检测响应的内容,甚至在已有 Content-Type 头的时候,也会根据返回的数据体内容,判断里面有没有 HTML 代码特征,如果有,返回的内容会直接被认定为 HTML 类型,而不顾实际的 Content-Type 头的类型设定。

如以下例子:

HTTP/1.1 200 OK 
Content-Length: 108 
Date: Thu, 26 Jun 2008 22:06:28 GMT 
Content-Type: text/plain; 

 
 
This page renders as HTML source code (text) in IE8. 
 

就会被早期一些的 IE 判定为 HTML 内容,最后以 HTML 方式被渲染呈现出来,尽管服务器端已经指定 Content-Type:text/plain; —— 这会导致一定的安全隐患。因为很多有交互功能的服务器,都会允许上传某些类型的“无害”文件,如图片和 mp3 等,如果在上传的图片内,巧妙地嵌入一定的 HTML 和 JavaScript 代码,最后能被渲染为 HTML 文件,显然会打破同源限制,产生安全隐患。

所以从 IE8 某个版本开始引入了 X-Content-Type-Options 这个新的响应头,如果这个响应头的值为 nosniff ,中文直译即「别嗅探」,就是告诉浏览器端,不要再主动猜测文档的类型了,以服务器端返回为准。后来 Chrome 等浏览器也支持这个响应头。

HTTP/1.1 200 OK 
Content-Length: 108 
Date: Thu, 26 Jun 2008 22:06:28 GMT 
Content-Type: text/plain;
X-Content-Type-Options: nosniff

这个响应头的 弊端

  • 某些早期浏览器不支持。

三、HTTP Strict Transport Security (HSTS) -- 不加密不舒服斯基

随着 WEB 传递的敏感信息越来越多,加密传输 HTTPS 也逐渐超越常规 HTTP,正越来越成为互联网上各大站点的首选传输协议。甚至像谷歌公司,从 2018 年中推出的 Chrome 68 版开始,就对所有 HTTP 访问提示安全警告了。以下引用自谷歌公司的声明:

> 过去几年中,我们一直主张站点采用 HTTPS,以提升其安全性。去年的时候,我们还通过将更大的 HTTP 页面标记为‘不安全’以帮助用户。

但通常使用者在浏览器里输入域名时,都是不带协议部分的,比如直接输入 www.tcxa.com.cn ,由浏览器补齐前面缺失的协议部分,变成完整的 URL: http://www.tcxa.com.cn 。浏览器默认加入的协议,总是 HTTP 的!对那些同时提供 HTTP 和 HTTPS 服务,但希望访问者优先使用 HTTPS 服务的站点来说,这种处理就不太符合他们的本意了。

为解决这个问题,标准制定机构2012 年又发布了一份:

> RFC 6797 > https://tools.ietf.org/html/rfc6797

这套机制就是 HTTP 严格传输安全响应头 (HTTP Strict Transport Security,简称 HSTS)

这个响应头里,最重要的一个参数项叫 max-age (单位为秒)。这个参数指的是,如果你上一次用 HTTPS 访问过该站点,下次再来访问,如果两次访问的间隔时间没有超过这个 max-age 的设定,第二次访问该站点时,浏览器就会直接强制以 HTTPS 协议访问了。这背后的逻辑是,第一次访问站点的时候,有针对性的攻击应该还不会发生。重点是防护后续的访问,所以后续的访问需要被强制升级为 HTTPS 协议。这个响应头需要在 HTTPS 流量里才有效,在 HTTP 流量里返回这个头并没有作用。

以下是站点 https://www.github.com 返回的部分响应头摘录——顺便一提,前面介绍的 X-Frame-OptionsX-Content-Type-Options 响应头也都出现在 github 里:

HTTP/1.1 200 OK
Cache-Control: max-age=0, private, must-revalidate
Content-Encoding: gzip
Server: GitHub.com
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
Expect-CT: max-age=2592000, report-uri="https://api.github.com/_private/browser/errors"
Transfer-Encoding: chunked
......
Vary: Accept-Encoding
X-Content-Type-Options: nosniff
X-Frame-Options: deny
......

其中 Strict-Transport-Security 响应头里, max-age 的值很大,这个 31536000 秒,其实等于 365 天,也就是一整年的时间。意思是除非前后两次访问 github 的时间超过一年,否则每次访问 github,都会被强制使用 HTTPS。 includeSubdomains 选项代表这个策略涵盖 github 所有的子域名。

还可以手工强制 HSTS。方式是在 Chrome 浏览器地址栏内,输入

chrome://net-internals/#hsts

来到如下图的配置界面:

图2

在「Add HSTS domain」里,手工加入需要强制使用 HTTPS 的域名。在「Query HSTS/PKP domain」里,则可以查询某个域名对这个响应头的设置。

这个响应头的 弊端

  • 某些早期浏览器不支持;
  • 如果 HTTPS 站点出现问题,导致无法访问, max-age 又设得过大,会导致使用者完全无法回退到访问 HTTP 站点。

四、浏览器兼容性

这些响应头基本上都是在客户端脚本越来越强大的 Web 2.0 时代之后才出现的,每种浏览器对它们的支持也各异,情况非常复杂混乱。所以使用前也需要评估目标人群的浏览器使用场景。

各种浏览器对这些响应头的兼容性,可以查询以下站点:

> https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers

如我们本篇提到的第一个响应头 X-Frame-Options 的兼容性情况如下:

图3

可以看到浏览器分电脑和手机版,电脑版里列出了从哪个版本的浏览器开始支持这个响应头,而且几个不同的选项值也略有差异。

五、这些响应头打哪儿来的?!

上面的一切看起来都很美好啦,但问题是:这些响应头难道是凭空出现的吗? 要怎么得到它们呢?一般的 WEB 服务器自身都支持响应头自定义设置。同时,各种 WEB 开发代码也可以通过编程的方式,实现更灵活的响应头返回和设置。WEB 开发代码较为复杂,无法囊括,我们大致介绍一下各种 WEB 服务器对这个功能的支持。

1. APACHE

例如,可以在Apache配置文件 httpd.conf 中添加以下配置,限制只有同源页面才可以嵌入iframe:

Headeralways append X-Frame-OptionsSAMEORIGIN

重启 apache 使其生效。其他的响应头也同理。关于Apache Header 指令的详细用法:

> http://httpd.apache.org/docs/current/mod/mod_headers.html#header

2. NGINX

例如,可以在 Nginx 配置文件 nginx.conf 中的「server」上下文内,添加以下配置,限制只有同源页面才可以嵌入 iframe:

add_header X-Frame-Options"SAMEORIGIN";

图4

重启 Nginx 服务使其生效。其他的响应头也类似同理。关于Nginx add_header 指令的详细用法:

> http://nginx.org/en/docs/http/ngxhttpheaders_module.html

3. IIS

在 WEB 站点对应的 web.config 中添加配置:

<system.webserver>
	<httpprotocol>
	<customheaders>
		<add name="X-Frame-Options" value="SAMEORIGIN" />**
	</customheaders>
	</httpprotocol>

   ...

</system.webserver>

如果不习惯改配置文件,可以使用图形控制台,如下选择网站对应的「HTTP 响应标头」:

图5

再根据实际需求,添加所需的响应头即可:

图6

(朱筱丹 | 天存信息)

Ref


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK