HTTP安全
HTTPS、SSL/TLS是什么
由于HTTP的两个缺陷——明文和不安全,是HTTP自身无法解决的,所以要引入新的HTTPS协议
为什么要有HTTPS
因为HTTP不安全
由于HTTP是明文的,所以传输过程完全透明,任何人都能在链路中截获、修改或者伪造请求/响应报文,导致数据不具有可信性
例如之前所学的代理服务,作为HTTP通信的中间人,可以随意修改请求
所以由于HTTP的明文特点,HTTP不安全的隐患很大,所以才要引入安全的通信
什么是安全
如果具备了四个特性,就可以认为是安全的:
-
机密性:指对数据的保密,只能由可信的人访问,对其他人是不可见的秘密
-
完整性:指数据在传输过程中没有被篡改,不多也不少,保持着原状
机密性虽然可以让数据加密,但是不能防止黑客对数据的修改
-
身份认证:指确认对方的真实身份,保证消息只能发送给可信的人
如果通信时另一方是假冒的网站,那么数据再保密也没有用
-
不可否认:不能否认已经发生过的行为
前三个特性可以解决安全通信的大部分问题,但是如果缺了不可否认,那通信的事务真实性就得不到保证
只有同时具备以上这四个特性,通信双方利益才有保障,才能算是真正的安全
什么是HTTPS
HTTPS默认端口号为443
其他绝大部分都沿用HTTP,语法、语义和HTTP完全相等,优缺点也照搬(明文和不完全除外)
HTTPS能够鉴别危险的网站,最大可能保证上网安全,防御黑客对信息的窃听、篡改、钓鱼、伪造
那HTTP为什么能做到机密性呢?
因为HTTPS中的S,代表的是HTTP下层的传输协议TCP/IP换成了SSL/TLS,让HTTP运行在安全的SSL/TLS协议上,收发报文不再使用Socket API,而是调用专门的安全接口
SSL/TLS
SSL即安全套接层,在OSI模型中处于第五层
SSL发展到v3时已经证明了是一个非常好的安全通信协议,后面将其改名为TLS(传输层安全)
TLS由记录协议、握手协议、警告协议、变更密码规范协议、扩展协议等几个子协议组成,综合使用了对称加密、非对称加密、身份认证等许多密码学前沿技术
浏览器和服务器在使用TLS建立连接时需要选择一组恰当的加密算法来实现安全通信,这些算法的组合被称为密码套件(加密套件)
TLS的密码套件命名遵循一定的规范格式:密钥交换算法+签名算法+对称加密算法+摘要算法
OpenSSL
这是一个著名的开源密码学程序库和工具包,几乎支持所有公开的加密算法和协议
对称加密与非对称加密
加密:把某种消息用某种方式转换成谁也看不懂的乱码,只有使用密钥才能转化出原始文本
明文:加密前的消息
密文:加密后的乱码
解密:使用密钥还原明文的过程,加密的反操作
加密算法:加密解密的操作过程
密钥:密钥是一长串数字,单位是位(bit),而不是字节(byte),加密方式可以分为对称加密和非对称加密
对称加密
对称加密就是指加密和解密用的钥匙都是同一个,只要保证密钥的安全,整个通信过程就可以说有了机密性
TLS中有许多对称加密算法可以选择,常用的有AES和ChaCha20
AES:高级加密标准,密钥长度可以是128、192、256,安全强度高,性能也很好,有的硬件还会做特殊优化,是应用最广泛的对称加密算法
ChaCha20:密钥长度固定256位,纯软件运行性能要超过AES,但是现在AES硬件优化,所以不再有明显优势
加密分组模式
可以让算法用固定长度的密钥加密任意长度的明文,将密钥转化成密文
最新的分组模式为AEAD,加密同时增加了认证的功能,常用GCM、CCM
这些组合起来,就可以得到TLS密码套件中定义的对称加密算法:
如ChaCha20-Poly1305意思是使用了ChaCha20算法,分组模式是Poly1305;AES128-GCM意思是密钥长度128位的AES算法,使用的分组模式是GCM
非对称加密
对称加密存在一个问题:如何把密钥安全地传递给对方,也就是密钥交换
如果密钥再传递途中被黑客窃取,那么就可以随意解密后面收发的数据,通信过程就没有机密性可言了
所以就出现了非对称性加密(公钥加密算法)
非对称性加密有两个密钥:公钥和私钥,公钥可以公开给任何人使用,私钥必须严格保密
但是两者有个特点:单向性,公钥加密后只能用私钥解密,私钥加密后只能用公钥解密
基于这个特点,就可以解决密钥交换的问题,网站秘密保管私钥,在网上任意分发公钥,想要登陆网站只要用公钥加密就可以了,密文只能由私钥持有者才能解密,所以黑客无法破解密文
非对称的加密算法在TLS中常用的有RSA和ECC
RSA:安全性基于整数分解的数学难题,使用两个超大素数的乘积作为密钥的材料,所以想要用公钥推出私钥是非常困难的
ECC:基于椭圆曲线离散对数的数学难题,使用特定曲线方程和基点生成公钥和私钥,子算法ECDHE用于密钥交换,ECDSA用于数字签名
ECC比RSA在安全强度上和性能上有明显的优势,因为位数小,密钥短,计算量和消耗内存和带宽少,解密的性能也就提升了
混合加密
由于非对称性加密算法的非常复杂,所以运算速度也非常慢,所以我们一般不会单独使用其中一种,而是会混合使用
这就是TLS中现在使用的混合加密方式:通信刚开始时使用非对称算法,解决密钥交换问题,然后用随机数生成对称算法使用的会话密钥,再用公钥加密,对方拿到密文之后用私钥解密,取出会话密钥,这样就实现了对称密钥的安全交换,后续就不用再用非对称加密了
这样就能解决对称加密的密钥交换问题,安全和性能兼顾,实现了机密性
数字签名和证书
在上一节中,我们解决了机密性的问题,但是现在安全性还不能保证
因为黑客虽然拿不到会话密钥,无法破解密文,但是还是可以窃听足够多的密文,并将其修改重组后发给网站,由于没有完整性保护,所以服务器只能全部接受,然后黑客就可以通过服务器的响应获取进一步的线索,最终破解出明文
除此之外,黑客还能伪造身份发布公钥,如果我们拿到假的公钥,混合加密就失效了
所以在机密性的基础上还必须加上完整性、身份认证等特性,才能实现真正的安全
摘要算法
摘要算法主要是实现完整性的
摘要算法就相当于一种特殊的压缩算法,把任意长度的数据压缩成固定长度、独一无二的摘要字符串
也就是说,摘要算法可以看出特殊的单向加密算法,加密后的数据无法解密,不能逆推出原文
由于摘要算法要防止冲突,可能会出现两份不同的原文对应相同的摘要,所以应该要让算法能抵抗冲突
现在TLS推荐的摘要算法是SHA-2,这是一系列摘要算法的统称,主要有SHA224、SHA256、SHA384等,能够分别生成相应固定字节的摘要
完整性
只要在原文后附上他的摘要,就能够保证数据的完整性
这样网站收到后会计算一下消息的摘要,比对一下原文是不是与摘要相对应,如果不对应则说明不可信
并且,完整性要建立在机密性之上,否则如果是明文传输的话,黑客可以修改消息后把摘要也一起改了,让网站鉴别不出完整性
数字签名
加密算法结合摘要算法,通信过程就比较安全了
但是现在还有漏洞,那就是通信的两个端点,黑客可以伪装成网站来窃取信息,所以网站要进行身份验证
TLS中的私钥只能由本人持有,而其他人不会有,所以可以当成数字签名
数字签名的原理:就是把公钥私钥用法反过来,之前是公钥加密、私钥解密,现在是私钥加密、公钥解密
并且由于非对称加密的效率太低,所以私钥只加密原文的摘要
签名和公钥一样完全公开,任何人都能获取,但是这个签名只有用私钥对应的公钥才能解开,拿到摘要后,再对比原文验证完整性就可以像签署文件一样证明消息确实是我们发的
只要我们和网站互相交换公钥,就可以用签名和验签来确认消息的真实性,因为私钥保密,黑客不能伪造签名,所以就能够保证通信双方的身份
数字证书和CA
现在综合使用对称加密、非对称加密、摘要算法,就可以实现安全的四大特性了
但是现在还要防止一个公钥信任问题,因为谁都可以发布公钥,所以防止黑客伪造公钥的手段
我们如果要用类似密钥交换的方法来解决公钥认证的问题,用别的私钥来给公钥签名,就会陷入无限循环,所以这时候我们必须引入一个第三方外力,构建起公钥的信任链
CA,就是证书认证机构,由他给各个公钥签名,用自己的信誉来保证公钥无法伪造,是可信的
CA对公钥的签名认证也是有格式的,不是简单的把公钥绑定在持有者身份上就可以,还要包含序列号、用途、颁发者、有效时间等,再把这些打包再签名,形成数字证书
CA证明自己的方式就是通过信任链,小一点的CA可以让大CA签名认证,链条最后就是Root CA,就只能自己证明自己了,叫做自证明证书或者根证书,这个必须相信,否则证书的信任链就走不下去了
证书体系的弱点
这里有两种情况:
- CA失误或者被欺骗,签发了错误的证书,虽然证书是真的,可他代表的网站确实假的
- 还有一种情况,CA被黑客攻陷,或者CA有恶意
所以我们需要给证书体系打上一些补丁:
- 针对第一种,开发出了CRL(证书吊销列表)和OCSP(在线证书状态协议) ,及时废止有问题的证书
- 对于第二种,只能操作系统或者浏览器从根上下狠手,撤销对CA的信任,拉入黑名单
TLS1.2连接过程
HTTPS建立连接
现在在浏览器地址栏中键入https开头的URI,再按下回车,会发生什么?
浏览器首先要从URI中提取出协议名和域名,因为协议名是https,所以浏览器知道端口号是默认的443,再用DNS解析域名,得到目标的IP地址,然后就可以使用三次握手与网站建立TCP连接了
在HTTP协议中,建立连接后就会直接发送请求报文,但是HTTPS还需要进行另外一次握手,在TCP上建立安全连接,然后才进行收发报文
这次握手过程与TCP有些类似,是HTTPS和TLS协议中最核心的部分
TLS协议组成
TLS协议包含几个子协议,比较常用的有记录协议、警报协议、握手协议、变更密码规范协议等
- 记录协议:规定了TLS收发数据的基本单位是记录,所有其他子协议都需要通过记录协议发出,多个记录数据可以在TCP包中一次发出,也不需要返回ACK
- 警报协议:向对方发出警报信息,类似HTTP协议中的状态码,收到警报的另一方可以选择继续,也可以终止连接
- 握手协议:这是TLS中最复杂的子协议,比TCP的SYN/ACK复杂得多,会在握手过程中协商TLS版本号、随机数、密码套件等信息,然后交换证书和密钥参数,最后双方协商得到会话密钥,用于后续的混合加密系统
- 变更密码规范协议:就是一个通知,告诉对方后续的数据都将使用加密保护
ECDHE握手过程
TLS1.3特性解析
TLS1.3三个主要改进目标:兼容、安全、性能
最大化兼容性
如果变更了头字段中的版本号,就会导致大量代理服务器和网关无法处理,最终导致TLS握手失败
所以为了兼容老设备,因为保持现有的记录格式不变,通过伪装实现兼容
这里我们用到了新的扩展协议,可以在记录末尾添加上一系列的扩展字段来增加功能,老版本的TLS不认识它可以直接忽略,就实现了向后兼容
在握手消息后面必须有supported_versions扩展,他标记了TLS的版本号,使用它就能区分新旧协议
强化安全
TLS1.3把很多漏洞和加密算法进行了修补
TLS1.3只保留了AES、ChaCha20对称加密算法,分组模式只能使用AEAD的GCM、CCM和Poly1305,摘要算法只能使用SHA256、SHA384,密钥交换算法只有ECDHE和DHE
提升性能
由于HTTPS建立连接时除了要做TCP握手,还要TLS握手,会多花两个消息往返,可能导致延迟
但是由于现在密码套件大幅度简化,也就没有必要像以前那样走复杂的协商流程,TLS1.3压缩了以前的Hello协商过程,效率提高了一倍
具体的做法是利用了扩展,客户端在Client Hello消息里直接用supported_groups带上了支持的曲线,用key_share带上曲线对应客户端公钥参数,用signature_algorithms带上签名算法
服务器收到后在这些扩展里选定一个曲线和参数,再用key_share扩展返回服务器这边的公钥参数,就是先了双方的密钥交换,后面流程就与1.2一样了
HTTPS的优化
HTTPS连接很慢,那么是什么原因呢?
HTTPS连接大致上可以划分为两个部分,第一个是建立连接时的非对称加密握手,第二个是握手后的对称加密报文传输
由于现在流行的AES、ChaCha20性能很好,还有硬件优化,所以报文传输的性能损耗很小
所以我们通常所说的HTTPS连接慢指的是刚开始建立连接那段时间
由于新增了一个TLS握手的步骤,这个握手的步骤最长可以花费两个消息往返,并且还有一些隐形的消耗,像非对称加密解密处理等,所以HTTPS在最差的情况下建立连接可能会比HTTP慢几百毫秒甚至几秒
硬件优化
HTTPS连接是计算密集型,所以我们可以选择更快的CPU,内建AES优化
还可以选择SSL加速卡,加解密时调用它的API,让专门的硬件来做非对称加解密,分担CPU计算压力
但是SSL加速卡也有一定的缺陷,比如升级慢,支持算法有限等
所以出现了第三种硬件加速方式:SSL加速服务器,用专门的服务器集群来解决
软件优化
软件优化的方式性价比更高一点
软件方面的优化可以分为两部分:软件升级和协议优化
软件升级在更新版本的时候都会做性能优化,修复错误,这种是最容易达成优化效果的
但是对于很多中大型公司来说,硬件升级和软件升级由于机器多,升级的时候可能有较大的风险,可能会影响到正常的线上服务,所以在软硬件升级都不可行的情况下,我们最常用的优化方式就是在现有环境下挖掘协议自身的协议
在协议优化中,尽量使用TLS1.3,因为其大幅度简化了握手的过程,如果不支持TLS1.3,那么握手时尽量使用椭圆曲线的ECDHE算法,不仅运算速度快,安全性还高
证书优化
握手过程中的证书验证也是一个比较耗时的操作,服务器需要把自己的证书链全部发给客户端,然后客户端接收后逐一验证
这里有两个优化点:证书传输和证书验证
服务器的证书可以选择椭圆曲线证书,因为比较小,节约带宽的同时也能减少客户端运算量
客户端的证书验证是个很复杂的操作,因为证书可能会被撤销失效,所以客户端有时候还回去访问CA,下载CRL或者OCSP数据,这又会产生DNS查询,建立连接等一系列网络通信,增加RTT
CRL是证书吊销列表,里面是被撤销信息的证书序号,随着吊销证书增多,列表会越来越大,所以每次连接网站实用性会降低
所以现在我们通常使用OCSP代替CRL,但是OCSP也要多出一次网络请求的消耗,而且还要以来CA服务器,如果CA服务器忙,那么响应延迟也等不起
所以又出来一个补丁,可以让服务器预先访问CA获取OCSP响应,握手时随着证书一起发给客户端,免去了客户端连接CA服务器查询的时间
会话复用
我们可以把主钥匙每次连接计算的结果缓存一下,就可以免去握手和计算的成本了
这就是会话复用,可以极大的提高HTTPS性能
会话复用分两种:Session ID是其中一种,客户端和服务器首次连接后会各自保存一个会话的ID号,内存中存储主密钥和其他相关的信息,当客户端再次连接时发一个ID过来,服务器就在内存中找,找到就直接用主密钥恢复会话状态,只用一个消息往返就可以建立安全通信
会话票证
这就是第二种会话复用:session_ticket
有点类似HTTP的Cookie,存储的责任由服务器转移到客户端,服务器加密会话信息,用New Session Ticket消息发给客户端,让客户端保存
这样重连的时候,服务器解密后验证有效期就可以恢复会话,开始加密通信
应该迁移到HTTPS吗
迁移的顾虑
阻碍HTTPS实施的因素主要有:慢、贵、难
慢:现在的客户端和服务器的运算能力已经有了很大的提升,性能方面已经没有问题了
贵:主要指证书申请和维护的成本太高,网站难以承担,主要是向CA申请证书贵,但是现在很多云服务厂商已经提供了价格低廉的证书,所以这个因素也不需顾虑
难:由于HTTPS涉及的知识点太多、太复杂、有一定的技术门槛,所以确实比较难
接下来我们来解决里面几个关键的难点
申请证书
申请证书可以使用Let's Encrypt这样的免费证书,比较方便
因为其一直在推动证书的自动化部署,还实现了专门的ACME协议
配置HTTPS
搞定了证书之后,就可以在443端口上开启HTTPS服务
在Nginx上实现非常简单,只要在listen后面加上ssl参数,再配上刚才的证书文件就可以实现最基本的HTTPS了
listen 443 ssl
为了提高HTTPS的安全系数和性能,还可以强制Nginx只支持TLS1.2以上的协议
sll_protocols TLSv1.2 TLSv1.3
密码套件的话,则优先使用服务器的套件,这样可以避免恶意客户端选择较弱的套件,降低安全等级,然后密码套件向TLS1.3看齐,只使用ECDHE、AES、ChaCha20
ssl_prefer_server_ciphers on
服务器名称指示
HTTP中,可以多个域名可以同时在一个IP地址上运行,这就是虚拟主机
但是在HTTPS中,因为请求头只有在TLS握手之后才能发送,在握手时就必须选择虚拟主机对应的证书,TLS无法得知域名的信息,就只能用IP地址来区分
所以最早的时候每个HTTPS域名必须使用IP地址,非常不方便
所以现在需要用TLS扩展,给协议加个SNI的补充条款,作用和Host字段差不多,客户端会在Client Hello时带上域名信息,这样服务器就可以根据名字而不是IP地址来选择证书了
重定向调整
由于现在HTTP还有很多人在使用,所以我们不能直接弃用HTTP站点
我们需要使用重定向跳转技术,把不安全的HTTP网址用301或302重定向到新的HTTPS网站
但是通过这种方式有两个问题,一个是重定向增加了网络成本,多了一次请求,另一个是存在安全隐患,重定向的响应可能会被中间人篡改,实现会话劫持,跳转到恶意网站
但是现在可以通过HSTS技术来消除这种安全隐患,HTTPS服务器需要再发出的响应头中添加一个Strict-Transport-Security字段,在设一个有效期,如:
Strict-Transport-Security: max-age=15768000; includeSubDomains
这相当于告诉浏览器:这个网站必须严格使用HTTPS协议,在半年之内都不允许使用HTTP
有了这个HSTS,以后浏览器在访问同样的域名的时候就会自动把URI中的http改成https,直接访问安全的HTTPS网站,所以这样中间人就失去了攻击的机会,也省去了一次跳转