前言
由于在TCP、UDP等方式传输数据时,数据包有可能被其他人截获,并解析出信息,这就给信息安全带来了很大的挑战。最初的SSL协议被网景公司提出,它不会影响上层协议(如HTTP、电子邮件等),但可以保证上层协议的通信安全。如果正确的使用SSL,第三方只能推断连接的两端地址、加密类型,以及数据频率和发送的大概数据量,但无法读取或修改任何实际数据。IETF后来在标准化SSL协议时,将其改为了TLS。很多人会混用SSL与TLS,但严格来说它们指代的协议版本不同(SSL3.0的升级版才是TLS1.0)。本文重在讲述TLS的概念和原理及其网络优化。
1.加密、身份验证与完整性
TLS协议的目标是为信息传输提供三个基本的保证:加密、身份验证和数据完整性。这三种服务并不是必须的,可以根据具体的应用场景进行选择。
加密:混淆数据的机制。
身份验证:验证身份标识有效性的机制。
完整性:检测消息是否被篡改或伪造的机制。
2.TLS握手
客户端与服务器在通过TLS交换数据之前,必须协商建立加密信道。协商内容包括:TLS版本、加密套件,必要时还要验证证书。其每次协商,都需要在客户端和服务端往返,大致过程如下:
0 ms:TLS运行在TCP基础之上,这意味着我们必须首先完成TCP 三次握手“ ,这需要一个完整的来回交互(RTT)。
56 ms:TCP连接建立后,客户端发送一些协商信息,如TLS协议版本,支持的密码套件的列表,和其他TLS选项。
84 ms:服务器挑选TLS协议版本,在加密套件列表中挑选一个密码套件,附带自己的证书,并将响应返回给客户端。可选的,服务器也可以发送对客户端的证书认证请求和其他TLS扩展参数。
112 ms:假设双方协商好一个共同的TLS版本和加密算法,客户端使用服务器提供的证书,生成新的对称密钥,并用服务器的公钥进行加密,并告诉服务器切换到加密通信流程。到现在为止,所有被交换的数据都是以明文方式传输,除了对称密钥外,它采用的是服务器端的公钥加密。
140 ms:服务器用自己的私钥解密客户端发过来的对称密钥,并通过验证MAC检查消息的完整性,并返回给客户端一个加密的“Finished”的消息。
168 ms:客户端采用对称密钥解密消息,并验证MAC,如果一切OK,加密隧道就建立好了。应用程序数据就可以发送了。
-
应用层协议协商
理论上,两个网络节点可能使用一个自定义的应用程序协议进行互相通信。解决这个问题的方法之一是在确定协议的前期,给它分配一个众所周知的端口(例如,端口80用于HTTP,TLS的端口443),并配置所有客户端和服务器使用它。然而,在实践中,这是一个缓慢和不切实际的过程:每个端口的分配必须批准,更糟的是防火墙及其他中间服务器往往只允许使用80和443进行通信。为了简化自定义协议的部署,需要重用80或443端口,再通过额外的机制协商确定协议。80端口被保留用于HTTP,HTTP规范提供了一个特殊的Upgrade流程来完成这个目标。然而,使用Upgrade可能带来额外的网络往返延迟,并在实际应用中往往因为许多中间服务器的存在是不可靠的。
既然80端口不太适合用来协商协议,那就使用443端口,这是给安全HTTPS会话保留的。端到端的加密隧道对中间设备模糊了数据,因此这就成为了一种可以快速和可靠的方式实现和部署任意的应用程序协议。然而,使用TLS解决了可靠性,我们仍然需要一种方式来协商应用协议!作为HTTPS会话,当然可以复用了HTTP的Upgrade机制来协商,但这会带来一个额外完整的往返延迟(RTT)。如果在把TLS握手的同时协商确定协议可行吗?
应用层协议谈判(ALPN)是一个TLS扩展,支持在TLS握手过程中进行协议协商,从而省去通过HTTP的Upgrade机制所需的额外往返延迟。过程如下:
- 客户在ClientHello消息添加新的ProtocolNameList字段,包含支持的应用程序协议列表。
- 该服务器检查ProtocolNameList字段,并在ServerHello消息中返回一个ProtocolName字段,用来指示服务器端选择的协议。
服务器可能只响应其中一个协议,如果它不支持任何客户端要求的协议,那么它可能选择中止连接。其结果是,TLS握手完成后,安全隧道建立好了,客户端和服务端也协商好了所使用的应用协议 - 它们可以立即开始通信。
-
服务器名称指示
任意两个TCP端之间都可以建立加密的TLS隧道:客户端只需要知道对端的IP地址就可以建立连接,并执行TLS握手。但是,如果服务器需要部署多个独立的网站,每个与自己的TLS证书,但使用同一个IP地址 - 请问如何处理?为了解决上述问题,SNI(服务器名称指示)扩展被引入到TLS协议中,它允许客户端在握手开始指示他想要连接的主机名。服务器检查SNI主机名,选择适当的证书,并继续握手。
注:TLS + SNI工作流程和HTTP的Host头域宣告流程是相同的,客户端在头域中指示它要请求的Host:同一IP地址可能会部署许多不同domain,SNI和Host都是用来区分不同的Host或者Domain。
3.TLS会话恢复
完整的TLS握手需要额外延迟和计算,为所有需要安全通信的应用带来了严重的性能损耗。为了帮助减少一些性能损耗,TLS提供恢复机制,即多个连接之间共享相同的协商密钥数据。
会话标识符
“会话标识符”(RFC 5246)恢复机制在SSL 2.0中首次被引入,支持服务器端创建32字节的会话标识符,并将其作为“ServerHello”消息的一部分进行发送。在服务器内部,服务器保存一个会话ID和其对应的协商参数。对应地,客户端也同时存储会话ID信息,在后续的会话中,可以在“ClientHello”消息中携带session ID信息,告诉服务器客户端还记着session ID对应的密钥和加密算法等信息,并且可以重用这些信息。假设在客户端和服务器都能在它们各自的缓存中找到共享的会话ID参数,那么就可以缩减握手了,如下图所示。否则,开始一个新的会话协商,生成新的会话ID。
借助会话标识符,我们能够减少一个完整的往返,以及用于协商的共享密钥的公钥加密算法开销。这让我们能快速的建立安全连接,而不损失安全性。然而,“会话标识符”机制的一个限制就是要求服务器为每个客户端创建和维护一个会话缓存。这会为服务器上带来几个问题,对于一些每天同时几万,甚至几百万的单独连接的服务器来说:由于缓存session ID所需要的内存消耗将非常大,同时还有session ID清除策略的问题。这对一些流量大的网站来说不是一个简单的任务,理想的情况下,使用一个共享的TLS会话缓存可以获得最佳性能。上述问题没有是不可能解决的,许多高流量的网站成功的使用了会话标识符。但是,对任何多服务主机的部署,会话标识符方案需要一些认真的思考和好的系统架构,以确保良好的的会话缓存。
会话记录单
由于在服务器访问量很大的情况下,缓存会话信息是一个很大的负担,为消除服务器需要维护每个客户端的会话状态缓存的要求,“Sesion Ticket”机制被引入--服务器端不再需要保存客户端的会话状态。如果客户端表明它支持Session Ticket,则在服务器完成TLS握手的最后一步中将包含一个“New Session Ticket”信息,这个信息包含一个加密通信所需要的信息,这些数据采用一个只有服务器知道的密钥进行加密。这个Session Ticket由客户端进行存储,并可以在随后的会话中添加到ClientHello消息的SessionTicket扩展中。因此,所有的会话信息只存储在客户端上,Session Ticket仍然是安全的,因为它是由只有服务器知道的密钥加密的。
会话标识符和会话记录单机制,通常分别被称为“会话缓存”和“无状态恢复”机制。无状态恢复的主要改进是消除服务器端的会话缓存,从而简化了部署,它要求客户在每一个新的会话开始时提供Session Ticket,直到Ticket过期。
注:在实际应用中,在一组负载平衡服务器中部署Session Ticket,也需要仔细考虑:所有的服务器都必须用相同的会话密钥,或者可能需要额外的机制,定期轮流在所有服务器上的共享密钥。
4.证书颁发与撤销
身份验证是建立每个TLS连接一个重要的组成部分。毕竟,TLS可以与任何端通过一个加密的隧道进行通信,包括攻击者,除非我们可以确信和我们通信的对方是可信任的,不然所有的加密工作都是无效的。如何证明某个主机是可信的呢?这就需要用证书,只有具有合法证书的主机才是可信。证书的来源有哪些呢?
- 手动指定的用户证书 :每一个浏览器和操作系统都提供了手动导入任何您信任的证书的机制。如何获得证书,并验证其完整性完全取决于你。
- 证书颁发机构 :证书颁发机构(CA)是一个值得信赖的第三方的机构(所有者),其证书值得信任。
- 浏览器和操作系统 :每个操作系统和大多数浏览器都包含了知名的证书颁发机构的列表。因此,你也可以信任这个软件的供应商,提供并维护的信任列表。
在实际应用中,手动验证为每一个网站的证书(尽管你可以,如果你是这样的倾向)是不切实际。因此,最常见的解决方案是借助证书颁发机构(CA)做这项工作(如下图) :在浏览器中指定哪些CA是可信任(根CA证书),CA负责验证你访问的每个网站,并进行审核,以确认这些证书没有被滥用或受损害。如果任何网站违反了CA的证书的安全性规定,那么CA有责任撤销其证书。
偶尔证书的颁发机构可能需要撤销或作废证书,这可能由于证书的私钥被攻破了,证书颁发机构本身被攻破,或者其他一些正常的原因譬如证书替换、证书签发机构发生变化,等等。为了解决这个问题,证书本身包含了检查是否已吊销的逻辑。因此,为了确保信任链不会受到攻击影响,每个节点都可以检查每个证书的状态,连同签名。
证书撤销名单(CRL):每个证书颁发机构维护并定期发布一份吊销证书序列号列表。要想验证证书的可靠性,直接查询CRL名单即可。
CRL文件本身可以定期公布,或在每次更新时都公布,CRL文件可以通过HTTP,或任何其他文件传输协议传输。该名单也是由CA签名,通常允许以指定的时间间隔缓存。在实际应用中,这个流程运行得很好,但也有一些场景CRL机制可能存在缺陷:
- 越来越多的撤销意味着CRL列表只会越来越长,每个客户端必须获取整个序列号列表
- 没有证书吊销即时通知机制 - 如果在客户端缓存期间,证书被吊销,客户端将认为证书是有效的,直到缓存过期。
在线证书状态协议(OCSP):提供一种实时检查证书状态的机制,支持验证端直接查询证书数据库中的序列号,从而验证证书是否有效。
OSCP占用带宽更少,支持实时验证,也带来了一些问题。如下:
- CA必须能够处理实时查询的实时性和负荷。
- CA必须确保该服务在任何时候全球都可用。
- 客户端在进行任何协商前都必须等待OCSP请求。
- 因为CA知道哪些网站的客户端访问,实时的OCSP请求可能暴露客户的隐私。
5.TLS记录协议
TLS记录协议主要用来识别TLS中的消息类型(通过“Content Type”字段的数据来识别握手,警告或数据),以及每个消息的完整性保护和验证。交付应用数据的典型流程如下:
- 记录协议接收到应用数据;
- 数据分块,每个块最大2^14即16 KB;
- 数据压缩(可选);
- 添加消息认证码(MAC)或HMAC(用于验证消息的完整性和可靠性);
- 使用协商的加密算法加密数据。
一旦上述步骤完成后,加密的数据被向下传递到TCP层进行传输。在接收端,采用反向相同的工作流程:使用协商的加密算法对数据进行解密,验证MAC,提取的应用数据给应用层。另一个好消息,所有上述的处理都是TLS层本身处理,对大多数应用程序是完全透明的。
当然,TLS记录协议也带来了一些重要限制:
- TLS记录的最大大小为16KB;
- 每个记录包含一个5字节的头部,MAC(SSLv3,TLS 1.0,TLS 1.1最多20个字节,TLS 1.2的多达32个字节),如果采用块加密算法则还有填充块(padding);
- 为了解密和验证每一块数据,必须保证所有数据都已收到。
6.TLS优化
-
计算成本
-
尽早完成(握手)
-
会话缓存与无状态恢复
-
TLS记录大小
-
TLS压缩
-
证书链的长度
-
OCSP封套
-
HTTP严格传输安全