gitbook/透视HTTP协议/docs/111940.md
2022-09-03 22:05:03 +08:00

188 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 29 | 我应该迁移到HTTPS吗
今天是“安全篇”的最后一讲我们已经学完了HTTPS、TLS相关的大部分知识。不过或许你心里还会有一些困惑
“HTTPS这么复杂我是否应该迁移到HTTPS呢它能带来哪些好处呢具体又应该怎么实施迁移呢
这些问题不单是你,也是其他很多人,还有当初的我的真实想法,所以今天我就来跟你聊聊这方面的事情。
## 迁移的必要性
如果你做移动应用开发的话那么就一定知道Apple、Android、某信等开发平台在2017年就相继发出通知要求所有的应用必须使用HTTPS连接禁止不安全的HTTP。
在台式机上主流的浏览器Chrome、Firefox等也早就开始“强推”HTTPS把HTTP站点打上“不安全”的标签给用户以“心理压力”。
Google等搜索巨头还利用自身的“话语权”优势降低HTTP站点的排名而给HTTPS更大的权重力图让网民只访问到HTTPS网站。
这些手段都逐渐“挤压”了纯明文HTTP的生存空间“迁移到HTTPS”已经不是“要不要做”的问题而是“要怎么做”的问题了。HTTPS的大潮无法阻挡如果还是死守着HTTP那么无疑会被冲刷到互联网的角落里。
目前国内外的许多知名大站都已经实现了“全站HTTPS”打开常用的某宝、某东、某浪都可以在浏览器的地址栏里看到“小锁头”如果你正在维护的网站还没有实施HTTPS那可要抓点紧了。
## 迁移的顾虑
据我观察阻碍HTTPS实施的因素还有一些这样那样的顾虑我总结出了三个比较流行的观点“慢、贵、难”。
所谓“慢”是指惯性思维拿以前的数据来评估HTTPS的性能认为HTTPS会增加服务器的成本增加客户端的时延影响用户体验。
其实现在服务器和客户端的运算能力都已经有了很大的提升,性能方面完全没有担心的必要,而且还可以应用很多的优化解决方案(参见[第28讲](https://time.geekbang.org/column/article/111287)。根据Google等公司的评估在经过适当优化之后HTTPS的额外CPU成本小于1%额外的网络成本小于2%可以说是与无加密的HTTP相差无几。
所谓“贵”,主要是指证书申请和维护的成本太高,网站难以承担。
这也属于惯性思维在早几年的确是个问题向CA申请证书的过程不仅麻烦而且价格昂贵每年要交几千甚至几万元。
但现在就不一样了为了推广HTTPS很多云服务厂商都提供了一键申请、价格低廉的证书而且还出现了专门颁发免费证书的CA其中最著名的就是“**Lets Encrypt**”。
所谓的“难”是指HTTPS涉及的知识点太多、太复杂有一定的技术门槛不能很快上手。
这第三个顾虑比较现实HTTPS背后关联到了密码学、TLS、PKI等许多领域不是短短几周、几个月就能够精通的。但实施HTTPS也并不需要把这些完全掌握只要抓住少数几个要点就好下面我就来帮你逐个解决一些关键的“难点”。
## 申请证书
要把网站从HTTP切换到HTTPS首先要做的就是为网站申请一张证书。
大型网站出于信誉、公司形象的考虑通常会选择向传统的CA申请证书例如DigiCert、GlobalSign而中小型网站完全可以选择使用“Lets Encrypt”这样的免费证书效果也完全不输于那些收费的证书。
“**Lets Encrypt**”一直在推动证书的自动化部署为此还实现了专门的ACME协议RFC8555。有很多的客户端软件可以完成申请、验证、下载、更新的“一条龙”操作比如Certbot、acme.sh等等都可以在“Lets Encrypt”网站上找到用法很简单相关的文档也很详细几分钟就能完成申请所以我在这里就不细说了。
不过我必须提醒你几个注意事项。
第一申请证书时应当同时申请RSA和ECDSA两种证书在Nginx里配置成双证书验证这样服务器可以自动选择快速的椭圆曲线证书同时也兼容只支持RSA的客户端。
第二如果申请RSA证书私钥至少要2048位摘要算法应该选用SHA-2例如SHA256、SHA384等。
第三出于安全的考虑“Lets Encrypt”证书的有效期很短只有90天时间一到就会过期失效所以必须要定期更新。你可以在crontab里加个每周或每月任务发送更新请求不过很多ACME客户端会自动添加这样的定期任务完全不用你操心。
## 配置HTTPS
搞定了证书接下来就是配置Web服务器在443端口上开启HTTPS服务了。
这在Nginx上非常简单只要在“listen”指令后面加上参数“ssl”再配上刚才的证书文件就可以实现最基本的HTTPS。
```
listen 443 ssl;
ssl_certificate xxx_rsa.crt; #rsa2048 cert
ssl_certificate_key xxx_rsa.key; #rsa2048 private key
ssl_certificate xxx_ecc.crt; #ecdsa cert
ssl_certificate_key xxx_ecc.key; #ecdsa private ke
```
为了提高HTTPS的安全系数和性能你还可以强制Nginx只支持TLS1.2以上的协议打开“Session Ticket”会话复用
```
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_timeout 5m;
ssl_session_tickets on;
ssl_session_ticket_key ticket.key;
```
密码套件的选择方面我给你的建议是以服务器的套件优先。这样可以避免恶意客户端故意选择较弱的套件、降低安全等级然后密码套件向TLS1.3“看齐”只使用ECDHE、AES和ChaCha20支持“False Start”。
```
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE+AES128:!MD5:!SHA1;
```
如果你的服务器上使用了OpenSSL的分支BorringSSL那么还可以使用一个特殊的“等价密码组”Equal preference cipher groups特性它可以让服务器配置一组“等价”的密码套件在这些套件里允许客户端优先选择比如这么配置
```
ssl_ciphers
[ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305];
```
如果客户端硬件没有AES优化服务器就会顺着客户端的意思优先选择与AES“等价”的ChaCha20算法让客户端能够快一点。
全部配置完成后,你可以访问“[SSLLabs](https://www.ssllabs.com/)”网站,测试网站的安全程度,它会模拟多种客户端发起测试,打出一个综合的评分。
下图就是GitHub网站的评分结果
![](https://static001.geekbang.org/resource/image/a6/1b/a662d410dfdaa8ab44b36cbb68ab8d1b.png)
## 服务器名称指示
配置HTTPS服务时还有一个“虚拟主机”的问题需要解决。
在HTTP协议里多个域名可以同时在一个IP地址上运行这就是“虚拟主机”Web服务器会使用请求头里的Host字段参见[第9讲](https://time.geekbang.org/column/article/100513))来选择。
但在HTTPS里因为请求头只有在TLS握手之后才能发送在握手时就必须选择“虚拟主机”对应的证书TLS无法得知域名的信息就只能用IP地址来区分。所以最早的时候每个HTTPS域名必须使用独立的IP地址非常不方便。
那么怎么解决这个问题呢?
这还是得用到TLS的“扩展”给协议加个**SNI**Server Name Indication的“补充条款”。它的作用和Host字段差不多客户端会在“Client Hello”时带上域名信息这样服务器就可以根据名字而不是IP地址来选择证书。
```
Extension: server_name (len=19)
Server Name Indication extension
Server Name Type: host_name (0)
Server Name: www.chrono.com
```
Nginx很早就基于SNI特性支持了HTTPS的虚拟主机但在OpenResty里可还以编写Lua脚本利用Redis、MySQL等数据库更灵活快速地加载证书。
## 重定向跳转
现在有了HTTPS服务但原来的HTTP站点也不能马上弃用还是会有很多网民习惯在地址栏里直接敲域名或者是旧的书签、超链接默认使用HTTP协议访问。
所以我们就需要用到第18讲里的“重定向跳转”技术了把不安全的HTTP网址用301或302“重定向”到新的HTTPS网站这在Nginx里也很容易做到使用“return”或“rewrite”都可以。
```
return 301 https://$host$request_uri; #永久重定向
rewrite ^ https://$host$request_uri permanent; #永久重定向
```
但这种方式有两个问题。一个是重定向增加了网络成本,多出了一次请求;另一个是存在安全隐患,重定向的响应可能会被“中间人”窜改,实现“会话劫持”,跳转到恶意网站。
不过有一种叫“**HSTS**”HTTP严格传输安全HTTP Strict Transport Security的技术可以消除这种安全隐患。HTTPS服务器需要在发出的响应头里添加一个“**Strict-Transport-Security**”的字段,再设定一个有效期,例如:
```
Strict-Transport-Security: max-age=15768000; includeSubDomains
```
这相当于告诉浏览器我这个网站必须严格使用HTTPS协议在半年之内182.5天都不允许用HTTP你以后就自己做转换吧不要再来麻烦我了。
有了“HSTS”的指示以后浏览器再访问同样的域名的时候就会自动把URI里的“http”改成“https”直接访问安全的HTTPS网站。这样“中间人”就失去了攻击的机会而且对于客户端来说也免去了一次跳转加快了连接速度。
比如如果在实验环境的配置文件里用“add\_header”指令添加“HSTS”字段
```
add_header Strict-Transport-Security max-age=15768000; #182.5days
```
那么Chrome浏览器只会在第一次连接时使用HTTP协议之后就会都走HTTPS协议。
## 小结
今天我介绍了一些HTTPS迁移的技术要点掌握了它们你就可以搭建出一个完整的HTTPS站点了。
但想要实现大型网站的“全站HTTPS”还是需要有很多的细枝末节的工作要做比如使用CSPContent Security Policy的各种指令和标签来配置安全策略使用反向代理来集中“卸载”SSL。
简单小结一下今天的内容:
1. 从HTTP迁移到HTTPS是“大势所趋”能做就应该尽早做
2. 升级HTTPS首先要申请数字证书可以选择免费好用的“Lets Encrypt”
3. 配置HTTPS时需要注意选择恰当的TLS版本和密码套件强化安全
4. 原有的HTTP站点可以保留作为过渡使用301重定向到HTTPS。
## 课下作业
1. 结合你的实际工作分析一下迁移HTTPS的难点有哪些应该如何克服
2. 参考上一讲你觉得配置HTTPS时还应该加上哪些部分
欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。
![unpreview](https://static001.geekbang.org/resource/image/db/ec/dbe386f94df8f69fc0b32d2b52e3b3ec.png)
![unpreview](https://static001.geekbang.org/resource/image/56/63/56d766fc04654a31536f554b8bde7b63.jpg)