来源:http://www.contextis.com/resources/blog/security-http-headers
前言
当谈论到web应用安全,我们经常考虑到的是:过滤用户的输入、加密渠道传递数据和使用安全的函数这些明显的点,而忽略在web安全上HTTP返回头部和现代web浏览器相结合产生的正面影响,这篇文章试图阐明HTTP Headers
对web安全的主动和被动影响。
主动安全
我们查阅OWASP推荐的HTTP Headers
,下面这些headers
能够被利用起来主动地增加web应用的安全性。
X-Frame-Options
这个头部属性是告诉浏览器某个页面是否应该被展示在另一个页面中(比如在IFRAME
标签中)。允许一个页面被加载在IFRAME
中会造成点击劫持攻击的风险。攻击中目标站点被载入在背景位置,对受害者隐藏。然后受害者被引诱去网站上执行点击动作(例如通过一个调查或者抽奖),这些点击被处在背景位置的目标站点秘密的执行了。如果受害者当前是登录了目标站点的,那这些点击是带着用户的session
被执行的。通过这样的方式能以用户身份执行命令,以及从用户资料里获取信息。
X-Frame-Options
可以被设置为以下值:
DENY
SAMEORIGIN
ALLOW-FROM
除非你的应用明确的需要在IFRAME
里载入页面,否则请设置该头部值为DENY
X-Frame-Options: DENY
如果你的应用使用IFRAME
只是载入应用本身包含的(同源)页面,那么你应该设置该头部值为SAMEORIGIN
X-Frame-Options: SAMEORIGIN
如果你的应用使用IFRAME
需要载入非同源下的地址,那么你应该明确定义外部的源
X-Frame-Options: ALLOW-FROM contextis.co.uk
请注意X-Frame-Options
头部的指令ALLOW-FROM
接受一个确定的域名。没有协议,没有端口,没有路径和没有通配符。
兼容矩阵:
Chrome | Firefox | Internet Explorer | Opera | Safari |
---|---|---|---|---|
4.0 | 3.6 | 8.0 | 10.5 | 4.0 |
Strict-Transport-Security
这个头部经常缩写成HSTS
(HTTP Strict Transport Security),告诉浏览器每当用户发送这个头部访问站点时强制使用HTTPS
连接。所有主流浏览器都支持这个特性,并且应该:
- 仅仅通过
HTTPS
连接站点 - 转换站点所有的
HTTP
引用为HTTPS
- 在
SSL
证书错误时拒绝载入站点
此外还需要注意这个头部只能通过HTTPS
响应进行设置,也就是说用户通过HTTPS
至少连接过一次该站点。除非你做了一些特殊的准备,(比如说将域名添加到浏览器的预置HSTS
域名列表)。同样重要的是要注意头部信息仅在一段时间内有效:过期时间以秒为单位,推荐如下设置半年,告诉浏览器去遵从STS
。
Strict-Transport-Security: max-age=15768000
如果这条规则应该扩展到覆盖所有子域,那么可以通过在这个头部里面加入属性includeSubDomains
:
Strict-Transport-Security: max-age=15768000; includeSubDomains
一些浏览器(至少Chrome, Firefox,IE11/Edge 和 Safari)都有一个预加载列表,里面的URL
都明确指定了他们想使用HSTS
,如果用户打算访问该列表的这些URL
,那么浏览器就会自动强制应用HSTS
规则,尽管是第一次连接,否则可能会受到中间人攻击。把你的网站添加到预加载列表需要把URL
提交到这个页面,并追加preload
命令在到该头部里。例如:
Strict-Transport-Security: max-age=15768000; includeSubDomains; preload
X-XSS-Protection
关于这个头部一直都饱受争议,甚至有人明确指定建议禁用它。那么,这个头部到底做了些什么呢?
设置
X-XSS-Protection: 1
这个头部的目的是告诉浏览器利用其XSS
防护。目前仅在Chrome,Internet Explorer 和 Safari浏览器有这样的内建引擎并且能识别该头部(Firefox似乎要依赖第三方插件NoScript
)。
可能在浏览器端去过滤一些造成攻击的恶意请求看起来挺不错的,但是,其实过滤非常困难。尤其是在有人尝试试探性的检测恶意代码,过滤它并且同时又尽力维护站点正常工作。这将会导致一些过滤被绕过,甚至给先前健康的站点引入跨站脚本攻击。
设置
X-XSS-Protection: 1; mode=block
告诉浏览器不渲染整个页面,而是显示空白页。但是即使这样,由于方法早期有缺陷,从而导致一些大型站点(如facebook.com,live.com 和 slack.com)明确禁用XSS
过滤器(X-XSS-Protection: 0
)。
所以尽管很难对这个头部给出一个明确的建议,似乎看起来变体X-XSS-Protection: 1; mode=block
的写法更加成熟些,而且已经摒弃了早期的缺陷。除此之外,针对跨站脚本攻击最好的防护仍然是过滤所有的输入和输出。
设置如下头部明确开启过滤器去过滤恶意的输入
X-XSS-Protection: 1;
设置如下头部阻止当前检测到恶意输入的站点
X-XSS-Protection: 1; mode=block
另外可以设置包含一个URL
的report
参数,如果Webkit-XSS-Auditors
(Chrome,Safari)其中之一检测到攻击就会发送一个POST
消息到URL
,消息包含事件的详细信息:
X-XSS-Protection: 1; mode=block; report=https://domain.tld/folder/file.ext
兼容矩阵
Chrome | Firefox | Internet Explorer | Opera | Safari |
---|---|---|---|---|
4.0 | 4.0 | 11 | 12 | 7 |
X-Content-Type-Options
这个头部用来阻止特定版本的IE浏览器“嗅探”页面的MIME
类型。它是IE浏览器的一个特性,当页面(Content-Type: text/plaintext
)包含HTML
标签时,浏览器将其作为HTML
解释。然而,当处理用户提供的内容时引入了跨站脚本攻击的风险。这个头部仅有一个设置项nosniff
,这样就阻止了浏览器嗅探页面的MIME
类型。
X-Content-Type-Options: nosniff
兼容矩阵
Chrome | Firefox | Internet Explorer | Opera | Safari |
---|---|---|---|---|
? | ? | 8 | ? | ? |
Public-Key-Pins
Public-Key-Pins
也叫作HTTP Public Key Pinning
(缩写为HPKP
)是相对较新的头部,还没有被广泛的使用。然而,它具有很强的安全潜能,因为它允许网站经营者指定(pin
,密钥)一个有效的证书,减少对CA的依赖,在过去这种方式已经被证明是容易受到攻击的(如任何CA都可以创建一个技术上可行且受信任的证书,但是并不是由你指定的)。和HSTS
一样,浏览器开始支持记住这个密钥,并且仅仅接受证书密钥和头部提供的秘钥匹配的网站连接。然而这意味着一个意想不到的证书改动会造成访问者被锁定在网站外。因此在第一个失败的时候很有必要提供一个可用的备份证书密钥。它也应该包含一个max-age
属性,还有指定剩余时间(秒为单位),请记住,对不知情的用户这也可能是锁定时间。
Public-Key-Pins: pin-sha256="<sha256>"; pin-sha256="<sha256>"; max-age=15768000;
该规则如果要被扩展到覆盖所有子域,那么这个头部能通过添加属性includeSubDomains
被扩展:
Public-Key-Pins: pin-sha256="<sha256>"; pin-sha256="<sha256>"; max-age=15768000; includeSubDomains
兼容矩阵
Chrome | Firefox | Internet Explorer | Opera | Safari |
---|---|---|---|---|
35 | 35 | - | 23 | - |
Content-Security-Policy (取代 X-Content-Security-Policy 和 X-WebKit-CSP)
Content-Security-Policy
(缩写为CSP
)是一个灵活的方式去指定站点哪些内容可能被执行,哪些内容不能被执行。现在的一个问题是浏览器并不知道哪个源值得信任,哪个源不能被信任。比如来自apis.google.com
的包含第三方JavaScript
是否值得信任?唯一合理的解决方式是白名单,由开发者指定合法的资源位置。下面是一个基本的例子如何允许来自于本地和apis.google.com
的JavaScript
Content-Security-Policy: script-src 'self' https://apis.google.com
CSP
有一些额外的关键字,允许进行非常细粒度的访问控制,此外还要注意CSP
是作为每个站点的模型,因此每一个站点都需要一个自己的HTTP
头部集。
兼容矩阵
Chrome | Firefox | Internet Explorer | Opera | Safari |
---|---|---|---|---|
14 | 4 | 10* /12 通过X-Content-Security-Policy | 15 | 5.1* /7.1 通过X-Webkit-CSP |
被动安全
下面要介绍的头部并不会主动启用任何安全相关的特性,而是被动地影响着安全,通常通过这些头部会泄露不必要展示的信息。
Cookie attributes (HttpOnly and Secure)
与cookie
相关的一些特殊属性经常被忽视,而设置这些属性能很大程度上减少cookie
被偷的风险。
HttpOnly
HttpOnly
属性告诉浏览器拒绝JavaScript
访问cookie
,这使得通过跨站脚本攻击更加困难获取访问cookie
Set-Cookie: cookie=xxxxxxxxx; HttpOnly
Secure
这个属性告诉浏览器仅在HTTPS
连接的情况下发送cookie
信息,这个应该是所有的会话和认证相关的cookie
规范,因为它可以轻松拦截通过未加密的HTTP
连接。
Set-Cookie: cookie=xxxxxxxxx; Secure
当然这些属性能被组合使用:
Set-Cookie: cookie=xxxxxxxxx; HttpOnly; Secure
Server / X-Powered-By
这两个头部会显示在用的服务器软件和它的版本号,这些头部可能在调试的适合很有用,但是对用户体验没有任何用处,应该要么被去掉或者减少到不泄露任何版本信息。
Caching directives
另一个经常被忽视的问题是浏览器缓存的敏感信息,浏览器经常存储网站元素到本地高速缓存以加速浏览体验。对一些不敏感的站点和元素比如图片或样式信息来说,这样是可以的,但是对于敏感信息(比如来自web
应用认证区域的页面)要避免这样做。这个问题在共享计算机环境(比如说办公室,学校,网咖)下变得更糟糕,其他用户可以很容易的接触到你的浏览器缓存。要告诉浏览器(可能是中间缓存,如代理)不存储任何东西在它的缓存里面需要使用如下命令:
Cache-Control: no-cache, no-store
Expires: 0
Pragma: no-cache
要注意的是经常遇到的命令Cache-Control: private
在共享计算机的环境中不能被认为是安全的,因为它允许浏览器去存储这些元素到他的缓存里面。
ETag
Entity Tag
(缩写ETag
)头部是作缓存用途的,服务器使用特殊的算法为它提供的每个文件版本计算一个单独的ETag
,浏览器然后能够请求服务器查看是否该ETag
文件仍然可用,如果可用,服务器返回一个304
状态码给浏览器,让它使用本地缓存的版本,否则服务器发送完整的资源和200
状态码。
虽然这是一个有用的头部,你会发现有时会在漏洞相关的文章或报告对它的引用。问题是Apache
的某些版本(2.3.14之前的版本中)用于披露对于在其默认的配置提供服务的文件索引节点。索引节点可用于进一步的攻击,例如通过网络文件系统(NFS
),使用这些INode
节点来创建文件句柄。有问题的默认配置已经在最近版本的Apache
得到了纠正,但是你扔应该确保在httpd.conf
里你相应的FileETag
设置不包含INode
属性。下面这行是好的设置:
FileETag MTime Size
下面这行不是:
FileETag INode MTime Size
X-Robots-Tag 和 Robots.txt
X-Robots-Tag
头部能被用告诉搜索引擎(支持该头部)页面或文件如何被索引。和robots.txt
或者robots
的meta
标签比起来,X-Robots-Tag
的优点是它的头部能被全局配置,还可以调整到一个非常精细的和灵活的水平(比如正则表达式匹配特定的URL
)。发送一个meta
标签和媒体文件是不可能的,发送HTTP
头部和媒体文件是没问题的。它还有优点就是在每个请求的基础上公开信息而不是在单一的文件上。试想一下,你不想让任何人知道的秘密目录,把它们列成一个清单在robots.txt
文件上?也许因为一个不好的想法就会让所有人马上知道你想隐藏什么–你可能只是发了一个连接在你的网站上。
所以你应该干脆抛弃robots.txt
而完全依赖X-Robots-Tag
呢?或许不会,应该在最大兼容性下将他们组合使用。然而要记住,robots.txt
应该仅仅包含你需要索引的文件和目录。你不应该列出哪些你想要隐藏的文件,相反的,可以放置一个不允许条目在robots.txt
文件里。
阻止一切的例子:
User-Agent: *
Disallow: /
告诉爬虫索引/Public/
目录下的所有文件,剩下的不让索引:
User-Agent: *
Allow: /Public/
Disallow: /
给不同的HTTP服务器添加自定义的头部
下面你能找到关于如何在不同的HTTP
服务器软件里设置静态自定义的HTTP
头部的例子。
Apache
对于Apache
,推荐使用模块mod_headers
去控制http
头。命令Header
能够被置于配置文件的任何地方,比如:
<Directory "/var/www/dir1">
Options +Indexes
Header set X-XSS-Protection “1; mode=block”
</Directory>
要更详细的了解关于如何使用mod_headers
设置HTTP
头的指导请参考这个页面
Internet Information Services (IIS)
对于IIS 6.0-10.0
,这里有两个方式设置自定义头部:
通过命令行
appcmd set config /section:httpProtocol /+customHeaders.[name='X-XSS-Protection',value='1; mode=block']
通过图形界面
打开
IIS Manager
,使用Conections面板找到你想要开启的头部合适的级别,双击HTTP Response Headers。现在单击Add…然后为这个头部设置你想要设置的名称和值。例子中名称将会是X-XSS-Protection
,值将会是1; mode=block
。
要更详细的了解关于如何使用IIS
设置HTTP
头的指导请参考这个页面
Lighttpd
对于Lighttpd
推荐使用模块mod_setenvs
去控制apache
头部,命令setenv.add-response-header
能够被置于配置文件的任何地方,比如:
setenv.add-response-header = (
"X-XSS-Protection" => "1; mode=Block"
)
要更详细的了解关于如何使用Lighttpd
设置HTTP
头的指导请参考这个页面
NGINX
对于NGINX
推荐使用模块ngx_http_headers_module
去控制HTTP
头,命令add_header
能够被置于配置文件中合适的位置,比如:
server {
listen 80;
server_name domain1.com www.domain1.com;
root html;
location / {
add_header X-XSS-Protection “1; mode=Block” always
}
}
要更详细的了解关于使用NGINX
设置HTTP
头的指导请参考这个页面
总结和结论
已经看到有或多或少的新的HTTP
头能够主动的促进站点安全。我们还能看到有几个公认的头部可能值得重新审视,以减少被泄露的信息量。
参考
- HTTP Strict Transport Security Pre-Load.
- Mozilla Developer Network – HTTP Headers.
- RFC6797: HTTP Strict Transport Security (HSTS).
- RFC7034: HTTP Header Field X-Frame-Options.
- RFC7469: Public Key Pinning Extension for HTTP.
PS:没有完全一字一句的翻译,有些内容翻译不出那种味道,部分内容按照自己的理解整理的。