概要:吐血整理!HTTPS相关的知识点,看这篇就够了。
本篇博客从安全的通信的四个特性开始,介绍完各种常见的加密方式(对称加密、非对称加密)后,为了方便理解HTTPS设计的原理尝试利用各种加密方式的组合一步步推导出一个完整安全的通信协议。后面对HTTPS的TLS握手、TLS协议的发展历史、HTTPS完整的通信过程进行了详细完整的说明。并且列举了各种HTTPS的优化方案,包括OSCP、会话复用等。在本篇博客的最后,进行了HTTPS配置的实战,帮助大家直上手HTTPS的完整配置过程。
关于HTTP协议的详情,可以参考博主上一篇博客: HTTP协议看这篇就够用了
什么是安全的通信过程?
通常认为如果通信的过程具备了4个特性,就认为通信是安全的。这四个特性是:机密性、完整性、身份认证和不可否认。一般来说只要满足前3个特性就可以解决大部分的通信问题,但是如果缺了 不可否认的特性,那么通信事务的真实性也得不到保证。
特性 | 说明 |
---|---|
机密性 | 对数据保密,只有可信的人能看懂。 |
完整性/一致性 | 保证数据从发送到接收能辨别数据是否被篡改,是否还是原状。 |
身份认证 | 消息只发送给可信的人 |
不可否认 | 不能否认自己发送信息的行为和信息的内容。 |
HTTP为啥不安全?
HTTPS全称是 HyperText Transfer Protocol over Secure Socket Layer,超文本传输安全协议,是互联网未来发展的趋势。HTTPS里用到了对称加密、非对称加密、摘要算法、数字签名、证书、认证中心等,来构建了符合四大特性的安全通信。
TLS全称传输安全层协议,Transport Layer Security Protocol,TLS/SSL是一种加密通道的规范。
SSL全称 Secure Sockets Layer 安全套接字协议
TLS/SSL的发展历史
版本 | 描述 |
---|---|
SSL 1.0 | 存在严重的安全漏洞,从未公开过 |
SSL 2.0 | 在1995年2月发布,但是存在数个严重的安全漏洞很快被3,0替代 |
SSL 3.0 | 1996年发布 |
TLS 1.0 | IETF对SSL3.0进行了标准化,并添加了少数机制 但是几乎和SSL3.0一样 |
TLS 1.1 | 在RFC 4346中定义,2006年4月发布 |
TLS 1.2 | 在RFC 5246中定义 2008年8月发布 |
TLS 1.3 | 在RFC 8446中定义,于2018年8发表。 |
TLS协议是由TLS记录协议和TLS握手协议叠加而成的
记录协议:TLS Record protocol
握手协议:TLS Handshaking Protocols
由于有了传输安全层TLS,所以HTTPS比HTTP的安全性大幅提高
非对称加密是计算机通信安全的基石,保证了加密数据不会被破解。
非对称加密: 非对称加密需要两个秘钥,公钥(public key) 和私钥(private key),公钥和私钥都是成对存在。公钥可以公开出去,这样就不需要通信的双方都维护大量的秘钥了,这样安全性就得到了保证。但是加密解密的速度不如非对称加密。
常用算法: RSA、DSA、ECDSA
要解决的问题:
缺陷:
缺陷:1. 每次都非对称加密解密效率过低。2. 如果截获了 服务器的公钥,那么服务器返回的数据对黑客仍然可见。
解决问题: 解决了内容可能被窃听的问题
仍然存在的缺陷:
客户端无法辨别对方是不是真是的服务器,还是黑客的服务器。如果黑客进行中间人攻击,对客户端伪装成服务器,对真正的服务器伪装成客户端,那么还是能获取到内容并且进行篡改。针对这种情况,HTTPS引入了 数字签名机制和证书机制,来保证了身份无法被伪装,并解决了内容被篡改的问题。
CA是证书的签发机构,是公钥基础设施(Public Key Infrastructure PKI)的核心。CA负责签发证书,认证证书、管理已颁发证书的机构。
HTTP协议的通信开始过程会经历DNS解析,然后TCP三次握手建立连接,然后就开始传输数据了。但是在HTTPS中,TCP连接建立后,还有一个SSL/TLS层握手的过程,用于在TCP连接之上建立安全连接。最后才开始收发HTTP报文。这个TLS握手的过程,就是HTTPS安全通信的核心!
TL握手的过程中传输的数据形式:明文->非对称加密->对称加密。TLS握手过程进行密钥交换的话,有两种密钥交换算法,一种称为ECDHE密钥交换算法,另一种是RSA密钥交换算法。现在基本上都是ECDHE密钥交换算法。
第一步 客户端发出明文请求 称为Client Hellow
包含这些信息:1.TLS协议版本号 2.一个随机数称为 Client random 3.客户端支持的加密方法 Cipher Suites(密码套件)
Handshake Protocol: Client Hello
Version: TLS 1.2 (0x0303)
Random: 1cbf803321fd2623408dfe…
Cipher Suites (17 suites)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
第二步 服务端确认加密方法给出证书和随机数 Server Hellow
Handshake Protocol: Server Hello
Version: TLS 1.2 (0x0303)
Random: 0e6320f21bae50842e96…
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
Handshake Protocol: Server Key Exchange
EC Diffie-Hellman Server Params
Curve Type: named_curve (0x03)
Named Curve: x25519 (0x001d)
Pubkey: 3b39deaf00217894e...
Signature Algorithm: rsa_pkcs1_sha512 (0x0601)
Signature: 37141adac38ea4...
第三步 客户端验证证书的有效性
第四步 Client Key Exchange 客户端生成一个椭圆曲线的公钥
Handshake Protocol: Client Key Exchange
EC Diffie-Hellman Client Params
Pubkey: 8c674d0e08dc27b5eaa…
到这一步,服务端客户端都有了秘钥交换的两个参数,Client Params 、Server Params ,然后互相使用ECDHE算法得出一个新的参数“Pre _Master" 可以理解为也是一个随机数。Pre_Master计算的过程十分复杂,但是保证了黑客就算得到了之前的参数也算不出这个随机数。
第五步 使用伪随机算法利用 Pre_Master、Client Random、Server Random 算出Master Secret
# PRF 伪随机数算法
master_secret = PRF(pre_master_secret, "master secret",ClientHello.random + ServerHello.random)
第六步 客户端发送Change Cipher Spec 和Finished消息
第七步 服务端发送 Change Cipher Spec 和Finished消息
和第六步一样,服务端也发送 Change Cipher Spec 和Finished消息相当于双方都验证一下加密解密是否OK,握手结束后,双方就使用对称加密方式来发送被加密的Htpp请求和响应了。
在8.1中描述的是如今主流的TLS握手方式,与传统的握手有两点不同
TLS 1.3 记录格式不变,向后兼容
Handshake Protocol: Client Hello
Version: TLS 1.2 (0x0303)
Extension: supported_versions (len=11)
Supported Version: TLS 1.3 (0x0304)
Supported Version: TLS 1.2 (0x0303)
TLS1.3 安全强化调整
算法精简的好处:“减肥瘦身”之后,TLS1.3 里只保留了 AES、ChaCha20 对称加密算法,分组模式只能用 AEAD 的 GCM、CCM 和 Poly1305,摘要算法只能用 SHA256、SHA384,密钥交换算法只有 ECDHE 和 DHE,椭圆曲线也被“砍”到只剩 P-256 和 x25519 等 5 种。
废除RSA密钥交换的好处: 之前浏览器默认使用ECDHE而不是RSA做秘钥交换,是因为它不具有**“前向安全”,**如果有黑客长期收集系统传输的报文,一旦某日获取或者破解了私钥,那么黑客就能解密之前所有的报文。这就是所谓的“今日截获,明日破解”。而ECDHE算法则是“一次一秘”就算某次被破解,其他历史消息仍是安全的。
TLS 1.3的性能提升
HTTP的性能优化的点在哪儿呢? 从HTTP和HTTPS的对比来看,HTTPS主要就是多了连接时的非对称加密握手,和握手后的对称加密报文传输。我们主要能进行优化的地方,就是在非对称加密握手部分。
首先明确:HTTPS连接是属于计算密集型的任务,而不是I/O密集型,所以如果升级硬件来提升性能,钱应该花在更快的CPU上,而不是网卡、带宽、ssd这种。
新版本的软件往往优化更好。例如linux内核由2.x升级到 4.x,nginx1.6升级到nginx1.16等等
ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:EECDH+CHACHA20;
ssl_ecdh_curve X25519:P-256;
HTTPS的证书机制决定了,HTTPS在建立连接过程中会使用服务端发回的证书,向CA机构来验证证书的真假,来保证访问的是真实的服务器,这一步也会带来很大的时延。优化这一步主要从证书的传输和证书的验证下手。
优化证书的传输
服务器的证书可以选择椭圆曲线(ECDSA)证书而不是 RSA 证书,因为 224 位的 ECC 相当于 2048 位的 RSA,所以椭圆曲线证书的“个头”要比 RSA 小很多,即能够节约带宽也能减少客户端的运算量,可谓“一举两得”。
这里的ECDSA和RSA是对称加密算法的区别,选用ECDSA非对称加密算法的证书更小。
优化证书的校验 OCSP Stapling(OCSP证书装订)
客户端校验证书过程很复杂,除了要公钥解密验证证书链,客户端有时候还要去访问CA,下载CRL或者OCSP数据,来判断证书是否过期或者被吊销。
HTTPS 建立连接的过程:先是 TCP 三次握手,然后是 TLS 一次握手。这后一次握手的重点是算出主密钥“Master Secret”,而主密钥每次连接都要重新计算,未免有点太浪费了,如果能够把“辛辛苦苦”算出来的主密钥缓存一下“重用”,不就可以免去了握手和计算的成本了吗?
Session ID 会话复用
原理:
缺陷: 服务端需要保留每个连接的会话数据,对于用户量大的网站来说就是大问题。加重服务器的负担。
Session Ticket 会话复用
原理:
缺陷: SessionTicket 方案,需要使用一个固定的秘钥文件,ticket_key 来加密Ticket,为了防止秘钥被破解,保证“前向安全” 秘钥文件需要定期轮换。
TLS 1.3的预共享秘钥
原理:
false start、session id、session ticket 等方式,只能实现1RTT的握手过程,但是TLS1.3进一步实现了0-RTT。原理和“Session Ticket”差不多,但在发送 Ticket 的同时会带上应用数据(Early Data),免去了 1.2 里的服务器确认步骤,这种方式叫“Pre-shared Key”,简称为“PSK”。
缺陷: 有重放攻击的风险,黑客截获了PSK数据后,反复向服务器发送。解决办法是消息里加入时间戳、nonce验证,或者"一次性票证"限制重复放。
在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 以上的协议
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_timeout 5m;
#打开“Session Ticket”会话复用
ssl_session_tickets on;
ssl_session_ticket_key ticket.key;
#开启服务器ssl套件优先,避免客户端使用较弱的套件
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,可以配置等价密码组, 客户端硬件没有 AES 优化,服务器就会顺着客户端的意思,优先选择与 AES“等价”的 ChaCha20 算法,让客户端能够快一点
ssl_ciphers [ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305];
可以访问 https://www.ssllabs.com/ 测试配置完成后的https网站的安全性
HTTP协议是支持多个域名在一个IP地址上运行的,这就是虚拟主机机制。WEB服务器是通过请求头的HOST字段,来区分访问的虚拟主机。
问题:HTTPS里TLS握手之后,才会开始发送头信息,但是握手的时候就需要验证域名的证书,怎么解决?
答:
Extension: server_name (len=19)
Server Name Indication extension
Server Name Type: host_name (0)
Server Name: www.chrono.com
很多旧的书签,或者是直接输入域名时会直接使用http协议访问,这时候就需要帮助用户重定向到HTTPS协议了。这在 Nginx 里也很容易做到,使用“return”或“rewrite”都可以。
nginx配置重定向示例
return 301 https://$host$request_uri; #永久重定向
rewrite ^ https://$host$request_uri permanent; #永久重定向
但是直接重定向会存在一些问题
HSTS(HTTP严格传输安全) 机制可以解决这个问题,原理是HTTPS发出的响应请求头里返回 **Strict-Transport-Security **字段,值是一个过期时间,告诉浏览器,以后访问我这个网站,直接使用https协议就好,不允许你先访问HTTP。这样不但节省了一次重定向,也让中间人攻击失去了机会。
add_header Strict-Transport-Security max-age=15768000; #182.5days