HTTPS&SSL/TLS

HTTP 有一些缺点,无状态,明文,不安全。

其中的“无状态”在加入 Cookie 后得到了解决。

而另两个缺点——“明文”和“不安全”仅凭 HTTP 自身是无力解决的,需要引入新的 HTTPS 协议。

而由于其不安全的特性,引入了HTTPS 。

由于 HTTP 天生“明文”的特点,整个传输过程完全透明,任何人都能够在链路中截获、修改或者伪造请求 / 响应报文,数据不具有可信性。

比如,“代理服务”。它作为 HTTP 通信的中间人,在数据上下行的时候可以添加或删除部分头字段,也可以使用黑白名单过滤 body 里的关键字,甚至直接发送虚假的请求、响应,而浏览器和源服务器都没有办法判断报文的真伪

因此HTTP不安全,HTTPS为其增加了安全的特性。

安全的定义

首先什么样的通信过程才是安全的呢?

通常认为,如果通信过程具备了四个特性,就可以认为是“安全”的,这四个特性是:机密性、完整性,身份认证和不可否认。

  • 机密性(Secrecy/Confidentiality)是指对数据的“保密”,只能由可信的人访问,对其他人是不可见的“秘密”,简单来说就是不能让不相关的人看到不该看的东西。

  • 完整性(Integrity,也叫一致性)是指数据在传输过程中没有被篡改,不多也不少,“完完整整”地保持着原状。

    机密性虽然可以让数据成为“秘密”,但不能防止黑客对数据的修改,黑客可以替换数据,调整数据的顺序,或者增加、删除部分数据,破坏通信过程。

  • 身份认证(Authentication)是指确认对方的真实身份,也就是“证明你真的是你”,保证消息只能发送给可信的人。

    如果通信时另一方是假冒的网站,那么数据再保密也没有用,黑客完全可以使用冒充的身份“套”出各种信息,加密和没加密一样。

  • 不可否认(Non-repudiation/Undeniable),也叫不可抵赖,意思是不能否认已经发生过的行为,不能“说话不算数”“耍赖皮”。

    使用前三个特性,可以解决安全通信的大部分问题,但如果缺了不可否认,那通信的事务真实性就得不到保证,有可能出现“老赖”。

所以,只有同时具备了机密性、完整性、身份认证、不可否认这四个特性,通信双方的利益才能有保障,才能算得上是真正的安全。

HTTPS

HTTPS为 HTTP 增加了刚才所说的四大安全特性。

HTTPS 其实是一个“非常简单”的协议,RFC 文档很小,只有短短的 7 页,里面规定了新的协议名“https”,默认端口号 443,至于其他的什么请求 - 应答模式、报文结构、请求方法、URI、头字段、连接管理等等都完全沿用 HTTP,没有任何新的东西。

HTTPS 凭什么就能做到机密性、完整性这些安全特性呢?

秘密就在于 HTTPS 名字里的“S”,它把 HTTP 下层的传输协议由 TCP/IP 换成了 SSL/TLS,由“HTTP over TCP/IP”变成了“HTTP over SSL/TLS”,让 HTTP 运行在了安全的 SSL/TLS 协议上,收发报文不再使用 Socket API,而是调用专门的安全接口。

下图是HTTP和HTTPS的结构对比图。

SSL/TLS

SSL 即安全套接层(Secure Sockets Layer),在 OSI 模型中处于第 5 层(会话层).

SSL 发展到 v3 时已经证明了它自身是一个非常好的安全通信协议,于是互联网工程组 IETF 在 1999 年把它改名为 TLS(传输层安全,Transport Layer Security),正式标准化.

TLS 由记录协议、握手协议、警告协议、变更密码规范协议、扩展协议等几个子协议组成,综合使用了对称加密、非对称加密、身份认证等许多密码学前沿技术。

OpenSSL

说到 TLS,就不能不谈到 OpenSSL,它是一个著名的开源密码学程序库和工具包,几乎支持所有公开的加密算法和协议,已经成为了事实上的标准,许多应用软件都会使用它作为底层库来实现 TLS 功能,包括常用的 Web 服务器 Apache、Nginx 等。

OpenSSL 是从另一个开源库 SSLeay 发展出来的,曾经考虑命名为“OpenTLS”,但当时(1998 年)TLS 还未正式确立,而 SSL 早已广为人知,所以最终使用了“OpenSSL”的名字。

小结

  1. 因为 HTTP 是明文传输,所以不安全,容易被黑客窃听或篡改;
  2. 通信安全必须同时具备机密性、完整性、身份认证和不可否认这四个特性;HTTPS 的语法、语义仍然是 HTTP,但把下层的协议由 TCP/IP 换成了 SSL/TLS;
  3. SSL/TLS 是信息安全领域中的权威标准,采用多种先进的加密技术保证通信安全;
  4. OpenSSL 是著名的开源密码学工具包,是 SSL/TLS 的具体实现。

关于加密

HTTPS的安全由TLS保证。

TLS为 HTTP 增加了机密性、完整性,身份认证和不可否认等特性

首先如果保证机密性?当然是通过加密

这里来简单谈谈加密.

首先,加密分为 : 对称加密,非对称加密,混合加密。

对称加密

不多说,就是加密解密使用的密钥都是同一个.

非对称加密的缺点 : 有密钥交换问题

(因为在对称加密算法中只要持有密钥就可以解密。如果你和网站约定的密钥在传递途中被黑客窃取,那他就可以在之后随意解密收发的数据,通信过程也就没有机密性可言了。)

常用的对称加密算法 : AES

非对称加密(公钥加密)

这种加密算法比较有意思 ,解决了对称加密中密钥的传输问题.

非对称加密是单向的 ,有两个密钥 :公钥和私钥

公钥给公开给所有人,私钥需要严格保密 .

公钥私钥虽然都可以用来加密解密,但公钥加密后只能用私钥解密,反过来,私钥加密后也只能用公钥解密。

通过这种特性,可以实现的是 : 加密 和 身份验证(具体过程不表,懂的都懂)

非对称加密的缺点 : 都是基于复杂的数学难题,运算速度很慢

常用的非对称加密算法: RSA

混合加密 - 机密性

单独的对称或非对称加密都有各自的缺点(密钥传输问题或计算慢),于是混合加密算法应运而生 .

这就是现在 TLS 里使用的混合加密方式,

方法 : 通过非对称加密将对称密钥发送过去,双方拿到对称密钥用这个密钥来加密会话

数字签名与证书

前面实现了加密性,在机密性的基础上还必须加上完整性、身份认证等特性,才能实现真正的安全

摘要算法(Digest Algorithm) - 完整性

实现完整性的手段主要是摘要算法(Digest Algorithm),也就是常说的散列函数、哈希函数(Hash Function)。

摘要算法可以近似地理解成一种特殊的压缩算法,它能够把任意长度的数据“压缩”成固定长度、而且独一无二的“摘要”字符串,就好像是给这段数据生成了一个数字“指纹”。

换一个角度,也可以把摘要算法理解成特殊的“单向”加密算法,它只有算法,没有密钥,加密后的数据无法解密,不能从摘要逆推出原文。

因此摘要算法保证了“数字摘要”和原文是完全等价的。所以,我们只要在原文后附上它的摘要,就能够保证数据的完整性。

即发送 原文+摘要 ,对方接受原文 + 摘要 ,对方只需要将原文也通过摘要算法进行计算,比对两份摘要,如果相同,则保证完整性 .

数字签名 - 身份认证

加密算法结合摘要算法,我们的通信过程可以说是比较安全了。但这里还有漏洞,就是通信的两个端点(endpoint)。

也就是目前还不能保证收发双方不是黑客. 因此还差一个东西来证明身份

如何证明 ? - 非对称加密

数字签名的原理其实很简单,就是把公钥私钥的用法反过来,之前是公钥加密、私钥解密,现在是私钥加密、公钥解密。签名和公钥一样完全公开,任何人都可以获取。但这个签名只有用私钥对应的公钥才能解开,拿到摘要后,再比对原文验证完整性,就可以像签署文件一样证明消息确实是你发的。

数字证书和CA

到现在,综合使用对称加密、非对称加密和摘要算法,我们已经实现了安全的四大特性

但是还是有问题 : “公钥的信任”问题

因为谁都可以发布公钥,我们还缺少防止黑客伪造公钥的手段,也就是说,怎么来判断这个公钥就是你或者某宝的公钥呢?

于是用类似密钥交换的方法来解决公钥认证问题,用别的私钥来给公钥签名, 陷入猜疑链 …….

所以要找一个公认的可信第三方,让它作为“信任的起点”,构建起公钥的信任链。

于是就有了 CA(Certificate Authority,证书认证机构).

CA 对公钥的签名认证也是有格式的,不是简单地把公钥绑定在持有者身份上就完事了,还要包含序列号、用途、颁发者、有效时间等等,把这些打成一个包再签名,完整地证明公钥关联的各种信息,形成“数字证书”(Certificate)

证书体系(PKI,Public Key Infrastructure)虽然是目前整个网络世界的安全基础设施,但绝对的安全是不存在的,它也有弱点,还是关键的“信任”二字。

如果 CA 失误或者被欺骗,签发了错误的证书,虽然证书是真的,可它代表的网站却是假的。

还有一种更危险的情况,CA 被黑客攻陷,或者 CA 有恶意,因为它(即根证书)是信任的源头,整个信任链里的所有证书也就都不可信了。

TLS1.2连接过程

当你在浏览器地址栏里键入“https”开头的 URI,再按下回车,会发生什么呢?

根据之前的博客<HTTP的连接\数据与缓存>应该知道:

  • 浏览器首先要从 URI 里提取出协议名和域名。因为协议名是“https”,所以浏览器就知道了端口号是默认的 443,
  • 它再用 DNS 解析域名,得到目标的 IP 地址,
  • 然后就可以使用三次握手与网站建立 TCP 连接了。
  • 在 HTTP 协议里,建立连接后,浏览器会立即发送请求报文。但现在是 HTTPS 协议,它需要再用另外一个“握手”过程,在 TCP 上建立安全连接,之后才是收发 HTTP 报文。

这个再次握手的过程是HTTPS和 TLS 协议里最重要、最核心的部分.

TLS协议组成

TLS 包含几个子协议,你也可以理解为它是由几个不同职责的模块组成,比较常用的有记录协议、警报协议、握手协议、变更密码规范协议等。

  • 记录协议(Record Protocol)规定了 TLS 收发数据的基本单位:记录(record)。它有点像是 TCP 里的 segment,所有的其他子协议都需要通过记录协议发出。但多个记录数据可以在一个 TCP 包里一次性发出,也并不需要像 TCP 那样返回 ACK。
  • 警报协议(Alert Protocol)的职责是向对方发出警报信息,有点像是 HTTP 协议里的状态码。比如,protocol_version 就是不支持旧版本,bad_certificate 就是证书有问题,收到警报后另一方可以选择继续,也可以立即终止连接。
  • 握手协议(Handshake Protocol)是 TLS 里最复杂的子协议,要比 TCP 的 SYN/ACK 复杂的多,浏览器和服务器会在握手过程中协商 TLS 版本号、随机数、密码套件等信息,然后交换证书和密钥参数,最终双方协商得到会话密钥,用于后续的混合加密系统。
  • 变更密码规范协议(Change Cipher Spec Protocol),它非常简单,就是一个“通知”,告诉对方,后续的数据都将使用加密保护。那么反过来,在它之前,数据都是明文的。

TLS连接过程(ECDHE)

下面的这张图简要地描述了 TLS 的握手过程,其中每一个“框”都是一个记录,多个记录组合成一个 TCP 包发送。所以,最多经过两次消息往返(4 个消息)就可以完成握手,然后就可以在安全的通信环境里发送 HTTP 报文,实现 HTTPS 协议。

所以可以发现,HTTPS 是应用层协议,需要先完成 TCP 连接建立,然后走 TLS 握手过程后,才能建立通信安全的连接。

握手简要图 :

如果用wireshark抓包,就可以看到一共四次握手:

下面这张是握手详细图 (HTTPS 常用的密钥交换算法有两种,分别是 RSA 和 ECDHE 算法。其中,RSA 是比较传统的密钥交换算法,它不具备前向安全的性质,因此现在很少服务器使用的。而 ECDHE 算法具有前向安全,所以被广泛使用):

  1. 在 TCP 建立连接之后,浏览器会首先发一个“Client Hello”消息,也就是跟服务器“打招呼”。里面有客户端的版本号、支持的密码套件,还有一个随机数(Client Random),用于后续生成会话密钥。(属于第一次握手)
  2. 服务器收到“Client Hello”后,会返回一个“Server Hello”消息。把版本号对一下,也给出一个随机数(Server Random),然后从客户端的列表里选一个作为本次通信使用的密码套件。(属于第二次握手)
  3. 然后,服务器为了证明自己的身份,就把证书也发给了客户端(Server Certificate)。(属于第二次握手)
  4. 接下来是一个关键的操作,因为服务器选择了 ECDHE 算法,所以它会在证书后发送“Server Key Exchange”消息,里面是椭圆曲线的公钥(Server Params),用来实现密钥交换算法,再加上自己的私钥签名认证。(属于第二次握手)
  5. 之后是“Server Hello Done”消息,服务器说:“我的信息就是这些,打招呼完毕。”(属于第二次握手)

这样第一个消息往返就结束了(两个 TCP 包),结果是客户端和服务器通过明文共享了三个信息:Client Random、Server Random 和 Server Params。

客户端这时也拿到了服务器的证书,那这个证书是不是真实有效的呢?

  1. 开始走证书链逐级验证,确认证书的真实性,再用证书公钥验证签名,就确认了服务器的身份
  2. 然后,客户端按照密码套件的要求,也生成一个公钥(Client Params),用“Client Key Exchange”消息发给服务器。(属于第三次握手)
  3. 现在客户端和服务器手里都拿到了密钥交换算法的两个参数(Client Params、Server Params),就用 ECDHE 算法一阵算,算出了一个新的东西,叫“Pre-Master”,其实也是一个随机数。
  4. 现在客户端和服务器手里有了三个随机数:Client Random、Server Random 和 Pre-Master。用这三个作为原始材料,就可以生成用于加密会话的主密钥,叫“Master Secret”。
  5. 主密钥有 48 字节,但它也不是最终用于通信的会话密钥,还会再用 PRF 扩展出更多的密钥,比如客户端发送用的会话密钥(client_write_key)、服务器发送用的会话密钥(server_write_key)等等,避免只用一个密钥带来的安全隐患。
  6. 有了主密钥和派生的会话密钥,握手就快结束了。客户端发一个“Change Cipher Spec”,然后再发一个“Finished”消息,把之前所有发送的数据做个摘要,再用会话密钥(master secret)加密一下,让服务器做个验证,验证加密通信「是否可用」和「之前握手信息是否有被中途篡改过」。。意思就是告诉服务器:“后面都改用对称算法加密通信了啊,用的就是打招呼时说的 AES,加密对不对还得你测一下。”(属于第三次握手)
  7. 服务器也是同样的操作,发“Change Cipher Spec”和“Finished”消息,双方都验证加密解密 OK,握手正式结束,后面就收发被加密的 HTTP 请求和响应了。(属于第四次握手)

HTTPS优化

HTTPS 连接大致上可以划分为两个部分:

  • 第一个是建立连接时的非对称加密握手
  • 第二个是握手后的对称加密报文传输。

通常所说的“HTTPS 连接慢”指的就是刚开始建立连接的那段时间。(对称加密算法如aes chacha20性能经过软硬件优化,性能已经足够好,目前https的瓶颈主要是建立连接的延迟)

在 TCP 建连之后,正式数据传输之前,HTTPS 比 HTTP 增加了一个 TLS 握手的步骤,这个步骤最长可以花费两个消息往返,也就是 2-RTT。而且在握手消息的网络耗时之外,还会有其他的一些“隐形”消耗,比如:

  • 产生用于密钥交换的临时公私钥对(ECDHE);
  • 验证证书时访问 CA 获取 CRL 或者 OCSP;
  • 非对称加密解密处理“Pre-Master”。

下图是TLS 握手过程中影响性能的部分

硬件优化

HTTPS 连接是计算密集型 。

  1. 首先,你可以选择更快的 CPU,最好还内建 AES 优化,这样即可以加速握手,也可以加速传输。
  2. 其次,你可以选择“SSL 加速卡”,加解密时调用它的 API,让专门的硬件来做非对称加解密,分担 CPU 的计算压力。不过“SSL 加速卡”也有一些缺点,比如升级慢、支持算法有限,不能灵活定制解决方案等。
  3. 所以,就出现了第三种硬件加速方式:“SSL 加速服务器”,用专门的服务器集群来彻底“卸载”TLS 握手时的加密解密计算,性能自然要比单纯的“加速卡”要强大的多。

软件优化

  1. 软件升级 。(软件在更新版本的时候都会做性能优化、修复错误)(但对于很多大中型公司来说,硬件升级或软件升级都是个棘手的问题,有成千上万台各种型号的机器遍布各个机房,逐一升级不仅需要大量人手,而且有较高的风险,可能会影响正常的线上服务。)
  2. 协议优化
    1. 如果有可能,应当尽量采用 TLS1.3,它大幅度简化了握手的过程,完全握手只要 1-RTT,而且更加安全。
    2. 只能用 1.2,那么握手时使用的密钥交换协议应当尽量选用椭圆曲线的 ECDHE 算法。(而不是RSA)
  3. 证书优化
    1. 证书传输优化。服务器的证书可以选择椭圆曲线(ECDSA)证书而不是 RSA 证书
    2. 证书验证优化。OCSP Stapling”(OCSP 装订),它可以让服务器预先访问 CA 获取 OCSP 响应,然后在握手时随着证书一起发给客户端,免去了客户端连接 CA 服务器查询的时间。

会话复用

硬件优化、软件优化、协议优化、证书优化,那么,还有没有其他更好的方式呢?

我们再回想一下 HTTPS 建立连接的过程:先是 TCP 三次握手,然后是 TLS 一次握手。这后一次握手的重点是算出主密钥“Master Secret”,而主密钥每次连接都要重新计算,未免有点太浪费了,如果能够把“辛辛苦苦”算出来的**主密钥缓存一下“重用”,不就可以免去了握手和计算的成本**了吗?

这种做法就叫“会话复用”(TLS session resumption),和 HTTP Cache 一样,也是提高 HTTPS 性能的“大杀器”,被浏览器和服务器广泛应用。

会话复用分为 : Session ID 和 Session Ticket

  • Session ID:就是客户端和服务器首次连接后各自保存一个会话的 ID 号,内存里存储主密钥和其他相关的信息。当客户端再次连接时发一个 ID 过来,服务器就在内存里找,找到就直接用主密钥恢复会话状态,跳过证书验证和密钥交换,只用一个消息往返就可以建立安全通信。

    但它也有缺点,服务器必须保存每一个客户端的会话数据,对于拥有百万、千万级别用户的网站来说存储量就成了大问题,加重了服务器的负担。

  • Session Ticket:有点类似 HTTP 的 Cookie,存储的责任由服务器转移到了客户端,服务器加密会话信息,用“New Session Ticket”消息发给客户端,让客户端保存。重连的时候,客户端使用扩展“session_ticket”发送“Ticket”而不是“Session ID”,服务器解密后验证有效期,就可以恢复会话,开始加密通信。

    不过“Session Ticket”方案需要使用一个固定的密钥文件(ticket_key)来加密 Ticket,为了防止密钥被破解,保证“前向安全”,密钥文件需要定期轮换,比如设置为一小时或者一天。

优化小结

  1. 可以有多种硬件和软件手段减少网络耗时和计算耗时,让 HTTPS 变得和 HTTP 一样快,最可行的是软件优化;
  2. 应当尽量使用 ECDHE 椭圆曲线密码套件,节约带宽和计算量,还能实现“False Start”;
  3. 服务器端应当开启“OCSP Stapling”功能,避免客户端访问 CA 去验证证书;
  4. 会话复用的效果类似 Cache,前提是客户端必须之前成功建立连接,后面就可以用“Session ID”“Session Ticket”等凭据跳过密钥交换、证书验证等步骤,直接开始加密通信。