最近看了一些文章,感觉不错,这里记录一下
之前基于nginx为网站配置了HTTPS服务,配置过程中涉及到两个知识点:
- 301 永久重定向
- HTST(HTTP Transport Security Protocol)
本文主要是通过梳理一下相关概念,理清这两种配置的目的。
1、HTTPS
HTTPS,也称作HTTP over TLS,TLS的前身是SSL。HTTPS 相比 HTTP 提供了数据完整性、 数据隐私性和身份认证的功能。
SSL通信过程
- 客户端发起一个HTTPS请求,提供客户端支持的加密算法和一个客户端随机数 client_random 给服务器
- 服务器返回给客户端配置好公钥的证书和服务器随机数 server_random
- 客户端验证公钥证书、比如是否在有效期内、域名是否匹配等;如果证书有效,客户端使用服务器提供的公钥加密加密随机数 premaster secret,发送给服务器
- 服务器使用自己的私钥解密 premaster secret
- 客户端和服务器将前面阶段的三个随机数,使用协商好的算法进行加密生成 master secret,后面双方通信使用这个对称密钥进行加密;
在nginx中的配置方法
- 申请证书
- 在nginx.conf中配置
server { listen 443; server_name www.example.com; #填写绑定证书的域名 ssl on; ssl_certificate /etc/nginx/sslconfig/1_www.example.com_bundle.crt; ssl_certificate_key /etc/nginx/sslconfig/2_www.example.com.key; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; ssl_prefer_server_ciphers on; location / { root html; #站点目录 index index.html index.htm; # proxy_pass http://$server_name:8920; }
中间人攻击(MitM)
中间人攻击(Man-in-the-middle attack,缩写:MITM)是指攻击者与通讯的两端分别建立独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制并进行数据篡改和嗅探。
- 代理服务器(mitmproxy):代理服务器 在HTTP和HTTPS通信中担任中间人的角色,在和客户端通信时它伪装成服务器,在和服务器通信时伪装成客户端,对两边的通信内容进行解码。代理服务器会生成伪造证书欺骗客户端,要使客户端信任伪造证书而不发出警告,需要在客户端手动将代理服务器注册为受信任的 CA。
- SSL剥离(SSLsplit):SSL剥离中,攻击者 SSLStrip 也是担任中间人的角色,中间人和服务器之间维持HTTPS连接,而和客户端维持HTTP连接,也就是将 HTTPS 降级为 HTTP。由于 HTTP 是明文传输的,因此攻击者可以窃取通信内容。
- 会话劫持(Session Hijacking):攻击者通过窃取或预测有效的会话令牌来破坏会话令牌,以获得对Web服务器的未授权访问。常用的窃取Cookie/会话令牌的方法有会话嗅探和跨站点脚本攻击等。
- 防御:对于实际生活中的信件沟通和线上的信息交流来说,中间人攻击都是很难防范的,一些小建议:
- 不要忽视浏览器弹出的证书警告!你可能访问的是钓鱼网站或者假冒的服务器
- 公共网络环境下(例如公共WiFi),没有HTTPS加密的敏感网站不要随便登录,一般不可信
- 在任何网站上登录自己的账号前确保网址是走的HTTPS加密协议
2、HSTS
既然建立HTTPS连接之前的这一次HTTP明文请求和重定向有可能被攻击者劫持,那么解决这一问题的思路自然就变成了如何避免出现这样的HTTP请求。我们期望的浏览器行为是,当用户让浏览器发起HTTP请求的时候,浏览器将其转换为HTTPS请求,直接略过上述的HTTP请求和重定向,从而使得中间人攻击失效,规避风险。其大致流程如下(略过HTTP请求和重定向,直接发送HTTPS请求):
- 第1步:用户在浏览器地址栏里输入网站域名,浏览器得知该域名应该使用HTTPS进行通信
- 第2步:浏览器直接向网站发起HTTPS请求
- 第3步:网站返回相应的内容
那么问题来了,浏览器是如何做到这一点的呢?它怎么知道那个网站应该发HTTPS请求,那个网站应该用HTTP请求呢?此时就该HSTS粉墨登场了。
使用HSTS
HSTS,即HTTP Strict-Transport-Security。 当站点通过 HTTPS 运行的时候,服务器通过返回一个响应头部 Strict-Transport-Security
,强制浏览器以后使用 HTTPS 进行通信。
HSTS最为核心的是一个HTTP响应头(HTTP Response Header)。正是它可以让浏览器得知,在接下来的一段时间内,当前域名只能通过HTTPS进行访问,并且在浏览器发现当前连接不安全的情况下,强制拒绝用户的后续访问要求。
HSTS Header的语法如下:
Strict-Transport-Security: <max-age=>[; includeSubDomains][; preload]
Strict-Transport-Security: max-age=<expire-time>
Strict-Transport-Security: max-age=<expire-time>; includeSubDomains
Strict-Transport-Security: max-age=<expire-time>; preload
其中:
- max-age是必选参数,是一个以秒为单位的数值,它代表着HSTS Header的过期时间,通常设置为1年,即31536000秒。
- includeSubDomains是可选参数,如果包含它,则意味着当前域名及其子域名均开启HSTS保护。
- preload是可选参数,只有当你申请将自己的域名加入到浏览器内置列表的时候才需要使用到它。关于浏览器内置列表,下文有详细介绍。
一个网站接受一个 HTTP 的请求,然后跳转到 HTTPS ,用户可能在开始跳转前,通过没有加密的方式和服务器对话,这样存在中间人攻击潜在威胁,跳转过程可能被恶意网站利用来直接接触用户信息,而不是原来的加密信息。
HTST 是在初次通过访问 HTTPS 连接并返回安全头之后,浏览器记录下这些信息。有效期内,当浏览器再次试图通过 HTTP 与服务器建立连接只会返回307 Temporary Redirect
,浏览器禁止加载 HTTP 信息,并将连接重定向到 HTTPS 。一个测试的结果如下:
在nginx中配置响应信息
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
浏览器返回结果
注意:
HTST 是通过 HTTPS 响应头设置的,如果使用 HTTP 则会被忽略。一种解决的策略是 preload HTST。HSTS预加载是把你的网站和或域名放在一个被认可的hsts列表上,这个列表实际上是内置在浏览器中的。Google提供了这个列表服务,并由Chrome、Firefox、Opera、Safari、IE11和Edge使用,可以将你的站点提交到官方的HSTS预加载列表。
让浏览器直接发起HTTPS请求####
只要在服务器返回给浏览器的响应头中,增加Strict-Transport-Security这个HTTP Header(下文简称HSTS Header),例如:
Strict-Transport-Security: max-age=31536000; includeSubDomains
就可以告诉浏览器,在接下来的31536000秒内(1年),对于当前域名及其子域名的后续通信应该强制性的只使用HTTPS,直到超过有效期为止。
完整的流程如下图所示(完整的HSTS流程):
只要是在有效期内,浏览器都将直接强制性的发起HTTPS请求,但是问题又来了,有效期过了怎么办?其实不用为此过多担心,因为HSTS Header存在于每个响应中,随着用户和网站的交互,这个有效时间时刻都在刷新,再加上有效期通常都被设置成了1年,所以只要用户的前后两次请求之间的时间间隔没有超过1年,则基本上不会出现安全风险。更何况,就算超过了有效期,但是只要用户和网站再进行一次新的交互,用户的浏览器又将开启有效期为1年的HSTS保护。
让浏览器强制拒绝不安全的链接,不给用户选择的机会
在没有HSTS保护的情况下,当浏览器发现当前网站的证书出现错误,或者浏览器和服务器之间的通信不安全,无法建立HTTPS连接的时候,浏览器通常会警告用户,但是却又允许用户继续不安全的访问。如下图所示,用户可以点击图中红色方框中的链接,继续在不安全的连接下进行访问。
(浏览器依然允许用户进行不安全的访问)
理论上而言,用户看到这个警告之后就应该提高警惕,意识到自己和网站之间的通信不安全,可能被劫持也可能被窃听,如果访问的恰好是银行、金融类网站的话后果更是不堪设想,理应终止后续操作。然而现实很残酷,就我的实际观察来看,有不少用户在遇到这样的警告之后依然选择了继续访问。
不过随着HSTS的出现,事情有了转机。对于启用了浏览器HSTS保护的网站,如果浏览器发现当前连接不安全,它将仅仅警告用户,而不再给用户提供是否继续访问的选择,从而避免后续安全问题的发生。例如,当访问Google搜索引擎的时候,如果当前通信连接存在安全问题,浏览器将会彻底阻止用户继续访问Google,如下图所示。
道高一尺魔高一丈:攻击者依然有可乘之机
细心的你可能发现了,HSTS存在一个比较薄弱的环节,那就是浏览器没有当前网站的HSTS信息的时候,或者第一次访问网站的时候,依然需要一次明文的HTTP请求和重定向才能切换到HTTPS,以及刷新HSTS信息。而就是这么一瞬间却给攻击者留下了可乘之机,使得他们可以把这一次的HTTP请求劫持下来,继续中间人攻击。
Preload List:让防御更加彻底
针对上面的攻击,HSTS也有应对办法,那就是在浏览器里内置一个列表,只要是在这个列表里的域名,无论何时、何种情况,浏览器都只使用HTTPS发起连接。这个列表由Google Chromium维护,FireFox、Safari、IE等主流浏览器均在使用。
3、301 redirect
HTTP 301 永久重定向
说明请求的资源已经被移动到了由 Location 头部指定的url上,是固定的不会再改变,搜索引擎会根据该响应修正,在 SEO 中301跳转对网站的权重会产生影响。
我们注意到,我们已经在nginx中已经配置了return 301 https://$server_name$request_uri
,将HTTP请求重定向到HTTPS。但是,在前面的浏览器测试结果中,通过HTTP访问并没有显示301永久重定向,而是307临时重定向。实际上,301 仍然是发生的,301是服务器层面上的重定向,而307是浏览器层面上的重定向。我们通过 httpstatus 进行测试,可以看到直观的结果:
我们已经说明了HSTS和301的作用,但是我们可能仍然有些困惑:
- 为什么已经使用 301重定向,还要使用 HSTS ?
- 为什么大多数网站使用301重定向,而不用预加载HSTS替代?
区别与联系
- HSTS覆盖域名,而301 redirect针对特定的URL Path;
- HSTS设计的意义是为了解决安全问题,而301更多是为了解决重定向问题,后来被广泛用在从 HTTP 升级到 HTTPS 的过程中,并对网站搜索权重造成影响;
- HSTS起作用是在至少访问过一次HTTPS且证书验证成功的情况下,在安全性要求较高的情况下我们强制使用 HTTPS,这需要进行预加载或者强制重定向;如果攻击者一直阻止网站建立HTTPS连接,HTST将不起作用;
- 目前大多数网站使用HSTS时是结合301 redirect使用的,而不是预加载,这可能是出于兼容考虑;
- HSTS相比301 redirect,在性能和安全上可能有一些优势。301缓存和其他请求一样都是保存在浏览器缓存中,重定向会消耗性能;而HSTS保留在另一个单独的缓存列表中,可以保留更长时间而不被浏览器清除;
- 中间人攻击可能利用重定向的时机进行介入,而HSTS 可以有效应对SSL剥离(HTTPS降级)导致的安全问题,但是对于其他类型的中间人攻击可能不起作用。
- HSTS 常和 HPKP(HTTP Public Key Pinning) 结合使用, HPKP 允许 HTTPS 网站指定其信任的证书,并指示浏览器不允许任何连接到由任何其他证书保护的站点。
总之,中间人攻击不可能简单地通过某一种策略来完全阻止,但是聊胜于无,HSTS 仍然是个可行的安全策略。
4、一些Tips
Tips 1:如何配置HSTS
很多地方都可以进行HSTS的配置,例如反向代理服务器、应用服务器、应用程序框架,以及应用程序中自定义Header。你可以根据实际情况进行选择。
常见的是在代理服务器中进行配置,以Nginx为例,只需在配置文件中加上下面这条指令即可:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
不过需要特别注意的是,在生产环境下使用HSTS应当特别谨慎,因为一旦浏览器接收到HSTS Header(假如有效期是1年),但是网站的证书又恰好出了问题,那么用户将在接下来的1年时间内都无法访问到你的网站,直到证书错误被修复,或者用户主动清除浏览器缓存。因此,建议在生产环境开启HSTS的时候,先将max-age的值设置小一些,例如5分钟,然后检查HSTS是否能正常工作,网站能否正常访问,之后再逐步将时间延长,例如1周、1个月,并在这个时间范围内继续检查HSTS是否正常工作,最后才改到1年。
Tips 2:如何加入到HSTS Preload List
根据官方说明,你的网站在具备以下几个条件后,可以提出申请加入到这个列表里。
具备一个有效的证书
在同一台主机上提供重定向响应,以及接收重定向过来的HTTPS请求
所有子域名均使用HTTPS
在根域名的HTTP响应头中,加入HSTS Header,并满足下列条件:
过期时间最短不得少于18周(10886400秒
必须包含includeSubDomains参数
必须包含preload参数
当你准好这些之后,可以在HSTS Preload
List的官网上(https://hstspreload.org)提交申请,或者了解更多详细的内容。
Tips 3:如何查询域名是否加入到了Preload List
从提交申请到完成审核,成功加入到内置列表 ,中间可能需要等待几天到几周不等的时间。可通过官网https://hstspreload.org或在Chrome地址栏里输入chrome://net-internals/#hsts查询状态。
Tips 4:不足
用户首次访问某网站是不受HSTS保护的。这是因为首次访问时,浏览器还未收到HSTS,所以仍有可能通过明文HTTP来访问。解决这个不足目前有两种方案,一是浏览器预置HSTS域名列表,Google Chrome、Firefox、Internet Explorer和Spartan实现了这一方案。二是将HSTS信息加入到域名系统记录中。但这需要保证DNS的安全性,也就是需要部署域名系统安全扩展。截至2014年这一方案没有大规模部署。
由于HSTS会在一定时间后失效(有效期由max-age指定),所以浏览器是否强制HSTS策略取决于当前系统时间。部分操作系统经常通过网络时间协议更新系统时间,如Ubuntu每次连接网络时,OS X Lion每隔9分钟会自动连接时间服务器。攻击者可以通过伪造NTP信息,设置错误时间来绕过HSTS。解决方法是认证NTP信息,或者禁止NTP大幅度增减时间。比如Windows 8每7天更新一次时间,并且要求每次NTP设置的时间与当前时间不得超过15小时。
参考
https://juejin.cn/post/6844903865788137479
https://blog.csdn.net/u014311799/article/details/79037717