1.http和https
http:超文本传输协议,它是以一种明文的方式(传输是明文的),没有任何加密,如果我们在该网站输入登录密码等信息,发送请求到服务器时,如果有人在中途截取我们的信息,那我们的信息可能就暴露了,默认80
https:超文本传输安全协议,利用SSL/TLS来加密数据包。默认443,需要到 CA 申请证书,一般免费证书很少,需要交费,握手阶段比较费时,会使页面的加载时间延长近50%,增加10%到20%的耗电;
对称密钥:直接发密钥,之后的通信都用过这个密钥打开。它的弊端就是,可能被中间人拦截,这样中间人就可以获取到了密钥,就可以对传输的信息就行窥视和篡改。
非对称密钥(RSA):双方必须协商一对密钥,一个私钥一个公钥。用私钥加密的数据,只有对应的公钥才能解密,用公钥加密的数据, 只有对应的私钥才能解密
但它的弊端就是,算法很慢
我们使用结合的方式!
使用 非对称密钥(RSA) 发送 对称密钥,然后双方使用 对称密钥 进行通信
SSL协议
这个协议简单来说就是提供数据安全和完整性的协议,简而言之,它是一项标准技术。比如我们访问了一个https网站,客户端和服务端会(握手阶段)如果某个网站受 SSL 证书保护,其相应的 URL 中会显示 HTTPS
- 先建立安全的连接通道,客户端先给出协议版本号 + 支持的加密方法 + 随机数等
- 确认信息后,服务器会生成一个加锁的箱子,但是这把锁有两把不一样的钥匙,一把留给服务器自己(私钥),一把提供给客户端(公钥)
- 服务器发送 — > 未上锁的箱子 + (里面放着一份网站的证书(证书包括公钥和数字签)到 – > 客户端(你访问的服务器没问题))
- 客户端生成两把对称密钥,一把留给自己,然后把信息 + 另一把对称密钥放到箱子中,用钥匙锁上(只能用私钥开)发送 —>服务器
- 然后服务器用另外一把钥匙(私钥)打开箱子,然后双方可以通过对称密钥通信了
在这个过程中,即使箱子被拦截了,也很难打开箱子
中间人攻击
因为传输非对称密钥的时候,服务端发送的是公钥,,有可能被中间人拦截后进行篡改
这时我们就需要数字证书,确定服务端的身份,一定是服务端发送的,而数字证书类似于我们的公证处
这篇文章可以生动形象地了解到 https 的原理 https://juejin.cn/post/6844903504046211079
(而我们熟知的自动打卡,有些就是利用中间人攻击的原理(fiddler抓包),模拟客户端和服务端通信,当然,它也通过不了数字证书的校验,fiddler伪造的数字证书,是建立在我们手动信任的基础上)
(所以连接陌生wifi、下载乱七八糟的软件,有可能会被装上乱七八糟的私发证书,从而出现中间人攻击)
数字证书
- 数字证书是什么东西?其实它就是一个 .crt 文件
- 数字证书是谁颁发的?由权威证书认证机构颁发,一般我们简称为 CA 机构
- 数字证书如何申请的:
- 服务器上生成一对公钥和私钥。然后将域名、申请者、公钥(注意不是私钥,私钥是无论如何也不能泄露的)等其他信息整合在一起,生成
.csr
文件。 .csr
文件发送给CA机构,收到申请后会验证申请者的组织和个人信息,然后 CA 就会使用散列算法对.csr
里的明文信息先做一个HASH,得到一个信息摘要,再用 CA 自己的私钥对这个信息摘要进行加密,生成一串密文,密文即是所说的 签名
- 服务器上生成一对公钥和私钥。然后将域名、申请者、公钥(注意不是私钥,私钥是无论如何也不能泄露的)等其他信息整合在一起,生成
- 客户端(浏览器)向服务端发出请求,服务端返回证书给客户端。
- 客户端拿到证书后,把证书里的签名与及明文信息分别取出来,然后会用自身携带的CA机构的公钥去解密签名,然后信息摘要1,然后再对明文信息进行HASH,得到一个信息摘要2,对比信息摘要1 和信息摘要2,如果一样,说明证书是合法的,也就是证书里的公钥是正确的。
- 使用到的算法:摘要算法(Digest Algorithm),也就是常说的散列函数、哈希函数(Hash Function)。
- 目前 TLS 推荐使用的是 SHA-1 的后继者:SHA-2。
- SHA-1架构类似于MD5得散列函数,接受任意大小的输入,生成160位消息散列,容易受到碰撞攻击,这个算法已经不再安全
- SHA-2 实际上是一系列摘要算法的统称,总共有 6 种,常用的有 SHA224、SHA256、SHA384,分别能够生成 28 字节、32 字节、48 字节的摘要。这个算法目前被认为是安全的,还被应用于广泛领域比如PGP、比特币
参考:https://network.51cto.com/art/202010/628890.htm
TLS协议
它建立在SSL 3.0协议规范之上,是SSL 3.0的后续版本
对比SSL的增强内容:
- 更安全的MAC算法
- 更严密的警报
- “灰色区域”规范更加明确定义
2.HSTS
HTTP 严格传输安全( HSTS ) 是一种策略机制
维基百科:
有助于保护网站免受协议降级攻击[1]和cookie 劫持等中间人攻击。它允许Web 服务器声明Web 浏览器(或其他符合要求的用户代理)应仅使用HTTPS连接自动与其交互,该连接提供传输层安全性(TLS/SSL),这与单独使用不安全的HTTP不同。HSTS 是IETF标准跟踪协议,在RFC 6797中指定 .
服务器通过名为“”的 HTTP 响应头字段将 HSTS 策略传达给用户代理Strict-Transport-Security
。[1] HSTS 策略指定用户代理仅应以安全方式访问服务器的时间段。[2]使用 HSTS 的网站通常不接受明文 HTTP,要么拒绝通过 HTTP 的连接,要么系统地将用户重定向到 HTTPS(尽管规范没有要求)。这样做的结果是无法执行 TLS 的用户代理将无法连接到该站点。
特殊
该保护仅在用户至少访问该网站一次后才适用,这依赖于首次使用的信任原则。此保护的工作方式是用户输入或选择指向指定 HTTP 的站点的URL,将自动升级到 HTTPS,而无需发出 HTTP 请求,从而防止发生 HTTP 中间人攻击。
也就是说,不幸的是,你第一次访问这个网站,你不受 HSTS 的保护。 如果网站向 HTTP 连接添加 HSTS 头,则该报头将被忽略。 这是因为攻击者可以在中间人攻击(man-in-the-middle attack)中删除或添加头部。 HSTS 报头不可信,除非它是通过 HTTPS 传递的。
MDN:
**HTTP Strict Transport Security**
(通常简称为HSTS)是一个安全功能,它告诉浏览器只能通过 HTTPS 访问当前资源,而不是HTTP。
语法
Strict-Transport-Security: max-age=<expire-time>
Strict-Transport-Security: max-age=<expire-time>; includeSubDomains
Strict-Transport-Security: max-age=<expire-time>; preload
max-age=<expire-time>
设置在浏览器收到这个请求后的
includeSubDomains
可选
如果这个可选的参数被指定,那么说明此规则也适用于该网站的所有子域名。
preload
可选
查看 预加载 HSTS 获得详情。不是标准的一部分。
HSTS应用场景
当你你想使用他们的免费 Wi-Fi。您是否注意到他们的 Wi-Fi 密码印在纸上并且从未更改过?
一个恶毒的黑客在同一家酒店预订了房间。他们正在窃听通过这个不安全的无线网络的所有连接。
该黑客可以通过 HTTP 捕获任何仅依赖 301 重定向以从 HTTP 切换到 HTTPS 的网站的网络流量。这种方法为黑客提供了一个机会之窗,可以剥离您的 SSL 加密并窃取有价值的数据,或者更糟糕的是,提供一个虚假的登录门户页面。
这就是为什么您的网站必须使用 HTTP 严格传输安全而不是 HTTPS。获得 SSL 证书是远远不够的。
数据可能与商店或房屋中的实物一样有价值,因此将它们锁定和保护同样重要。挂锁您的网站有时是不够的,因为人们仍然会找到通过 http:// 访问您的网站的方法。HSTS 强制浏览器和应用程序连接使用 HTTPS(如果可用)。即使有人只是输入 www 或 http://。
设置从 http:// 到 https:// 的 301 重定向不足以完全保护您的域名。HTTP 的不安全重定向仍然存在机会之窗。
协议的支持保护才是有效的保护措施的
$ curl –head http://www.facebook.com HTTP/1.1 301 永久移动位置:https://www.facebook.com/
黑客仍然能够捕获站点 cookie、会话 ID(通常作为 URL 参数发送)或强制重定向到看起来与您的网站完全相同的网络钓鱼站点。哎哟!
通过安装 Strict-Transport-Security 标头,坏人几乎不可能收集到任何信息!甚至没有你的瑜伽时间表!
$ curl –head https://www.facebook.com HTTP/1.1 200 OK Strict-Transport-Security: max-age=15552000; preload
HSTS历史
价值数十亿美元的公司 Google 于 2016 年 7 月 29 日正式推出了 HSTS 安全策略。
HSTS 项目最早于 2009 年起草。2015年 6 月 8 日,首席信息官托尼·斯科特 (Tony Scott)发出了一份备忘录 ,而早在 2015 年就一直在进行更强有力的努力。
HSTS 永久要求
您的网站必须具有有效的 SSL 证书。您可以在GlobalSign 的 SSL Checker 上检查您的 SSL 的有效性。
使用 301 永久重定向将所有 HTTP 链接重定向到 HTTPS。
您的 SSL 证书必须涵盖所有子域。考虑订购通配符证书。
在基域上为 HTTPS 请求提供 HSTS 标头。
Max-age 必须至少为 10886400 秒或 18 周。如上所述,争取两年的价值!
如果您有 includeSubDomains 指令,则必须指定它们!
必须指定预加载指令。
HSTS工作原理
通常,当您在 Web 浏览器中输入 URL 时,您会跳过协议部分。 例如,你输入的是 www.acunetix.com
,而不是 http://www.acunetix.com
。 在这种情况下,浏览器假设你想使用 HTTP 协议,所以它在这个阶段发出一个 HTTP 请求 到 www.acunetix.com
,同时,Web Server 会返回 301 状态码将请求重定向到 HTTPS 站点。 接下来浏览器使用 HTTPS 连接到 www.acunetix.com
。 这时 HSTS 安全策略保护开始使用 HTTP 响应头:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
响应头的 Strict-Transport-Security
给浏览器提供了详细的说明。 从现在开始,每个连接到该网站及其子域的下一年(31536000秒)从这个头被接收的时刻起必须是一个 HTTPS 连接。 HTTP 连接是完全不允许的。 如果浏览器接收到使用 HTTP 加载资源的请求,则必须尝试使用 HTTPS 请求替代。 如果 HTTPS 不可用,则必须直接终止连接。
此外,如果证书无效,将阻止你建立连接。 通常来说,如果 HTTPS 证书无效(如:过期、自签名、由未知 CA 签名等),浏览器会显示一个可以规避的警告。 但是,如果站点有 HSTS,浏览器就不会让你绕过警告。 若要访问该站点,必须从浏览器内的 HSTS 列表中删除该站点。
https握手流程
一个请求从零开始所需要经历的RTT
流程 | 消耗时间 | 总计 | 可否缓存 |
---|---|---|---|
1. DNS 解析网站域名 | 1-RTT | 可缓存(第一次之后) | |
2. 访问 HTTP 网页 TCP 握手 | 1-RTT | 网站做了HSTS可忽略 | |
3. HTTPS 重定向 302 | 1-RTT | 网站做了HSTS可忽略 | |
4. 访问 HTTPS 网页 TCP 握手 | 1-RTT | ||
5. TLS 握手第一阶段 Say Hello | 1-RTT | ||
6. 【证书校验】CA 站点的 DNS 解析 | 1-RTT | 可缓存(本地) | |
7. 【证书校验】CA 站点的 TCP 握手 | 1-RTT | 可缓存(本地) | |
8. 【证书校验】请求 OCSP 验证 | 1-RTT | 可缓存(本地) | |
9. TLS 握手第二阶段 加密 | 1-RTT | ||
10. 第一个 HTTPS 请求 | 1-RTT | ||
10-RTT |
一般来说,比较积极的TCP在第三次握手的时候,已经顺便携带了数据,所以本来三次握手 =》 1.5RTT可以变成 1 RTT)
上面这 10 步是最最完整的流程,一般有各种缓存不会经历上面每一步。如果有各种缓存,并且有 HSTS 策略,所以用户每次访问网页都必须要经历的流程如下:
流程 | 消耗时间 | 总计 |
---|---|---|
1. 访问 HTTPS 网页 TCP 握手 | 1-RTT | |
2. TLS 握手第一阶段 Say Hello | 1-RTT | |
3. TLS 握手第二阶段 加密 | 1-RTT | |
4. 第一个 HTTPS 请求 | 1-RTT | |
4-RTT |
除去 4 是无论如何都无法省掉的以外,剩下的就是 TCP 和 TLS 握手了。 TCP 想要减至 0-RTT,目前来看有点难。那 TLS 呢?目前 TLS 1.2 完整一次握手需要 2-RTT,能再减少一点么?答案是可以的。
TLS1.2握手流程
缩减版在上方的 SSL协议 介绍过
第一个RTT:
Client 发送一个 ClientHello 消息,Server 必须回应一个 ServerHello 消息或产生一个验证的错误并且使连接失败。
ClientHello 和 ServerHello 建立了如下的属性: 协议版本,会话 ID,密码套件,压缩算法。此外,产生并交换两个随机数: ClientHello.random 和 ServerHello.random。
- 根据密钥协商算法的不同,交换的信息顺序等细微差别都会存在些许
- client、server的随机数是为了避免重放攻击
Server 会在 Certificate 消息中发送它自己的证书,如果它即将被认证。此外,如果需要的话,一个 ServerKeyExchange 消息会被发送(例如, 如果 Server 没有证书, 或者它的证书只用于签名,RSA 密码套件就不会出现 ServerKeyExchange 消息)。如果 Server 被认证过了,如果对于已选择的密码套件来说是合适的话,它可能会要求 Client 发送证书。接下来,Server 会发送 ServerHelloDone 消息,至此意味着握手的 hello 消息阶段完成
Server等待 Client 的响应
如果 Server 发送了一个 CertificateRequest 消息,Client 必须发送 Certificate 消息。现在 ClientKeyExchange 消息需要发送, 这个消息的内容取决于 ClientHello 和 ServerHello 之间选择的公钥算法。如果 Client 发送了一个带签名能力的证书, 则需要发送以一个数字签名的 CertificateVerify 消息,以显式验证证书中私钥的所有权。
- 一个非匿名的 Server 可以选择性地请求一个 Client 发送的证书
- 注: 一个匿名 Server 请求认证 Client 会产生一个致命的 handshake_failure 警告错误。
- Client 证书的数据结构和 Server Certificate 是相同的。
第二个RTT
这时,Client 发送一个 ChangeCipherSpec 消息,并且复制 pending 的 Cipher Spec 到当前的 Cipher Spec 中. 然后 Client 在新算法, 密钥确定后立即发送 Finished 消息。作为回应,Server 会发送它自己的 ChangeCipherSpec 消息, 将 pending 的 Cipher Spec 转换为当前的 Cipher Spec,在新的 Cipher Spec 下发送 Finished 消息。这时,握手完成,
Finished 消息是第一个被刚刚协商的算法,密钥和机密保护的消息。Finished 消息的接收者必须验证内容是正确的。一旦一方已经发送了 Finished 消息且接收并验证了对端发送的 Finished 消息,就可以在连接上开始发送和接收应用数据。
Finished 子消息的存在的意义:在所有的握手协议中,所有的子消息都没有加密和完整性保护,消息很容易篡改,改掉以后如果不检验,就会出现不安全的攻击。为了避免握手期间存在消息被篡改的情况,所以 Client 和 Server 都需要校验一下对方的 Finished 子消息。
如果中间人在握手期间把 ClientHello 的 TLS 最高支持版本修改为 TLS 1.0,企图回退攻击,利用 TLS 旧版本中的漏洞。Server 收到中间人的 ClientHello 并不知道是否存在篡改,于是也按照 TLS 1.0 去协商。握手进行到最后一步,校验 Finished 子消息的时候,校验不通过,因为 Client 原本发的 ClientHello 中 TLS 最高支持版本是 TLS 1.2,那么产生的 Finished 子消息的 verify_data 与 Server 拿到篡改后的 ClientHello 计算出来的 verify_data 肯定不同。至此也就发现了中间存在篡改,握手失败。
Client Server
ClientHello -------->
ServerHello
Certificate*
ServerKeyExchange*
CertificateRequest*
<-------- ServerHelloDone
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
Finished -------->
[ChangeCipherSpec]
<-------- Finished
Application Data <-------> Application Data
证书类型
密钥交换算法 | 证书类型 |
---|---|
RSA RSA_PSK | 证书中包含 RSA 公钥,该公钥可以进行密码协商,也就是使用 RSA 密码协商算法;证书必须允许密钥用于加密(如果 key usage 扩展存在的话,则 keyEncipherment 位必须被设置,表示允许服务器公钥用于密码协商) 注:RSA_PSK 定义于 [TLSPSK] |
DHE_RSA ECDHE_RSA | 证书中包含 RSA 公钥,可以使用 ECDHE 或者 DHE 进行密钥协商;证书必须允许密钥使用 Server 密钥交互消息中的签名机制和 hash 算法进行签名 (如果 key usage 扩展存在的话,digitalSignature 位必须设置,RSA 公钥就可以进行数字签名) 注: ECDHE_RSA定义于 [TLSECC] |
DHE_DSS | 证书包含 DSA 公钥; 证书必须允许密钥用于使用将在 Server 密钥交换消息中使用的散列算法进行签名 |
DH_DSS DH_RSA | 证书中包含 DSS 或 RSA 公钥,使用 Diffie-Hellman 进行密钥协商; 如果 key usage 扩展存在的话,keyAgreement 位必须设置,目前这种套件已经很少见了。 |
ECDH_ECDSA ECDH_RSA | 证书包含 ECDSA 或 RSA 公钥,使用 ECDH-capable 进行密钥协商。由于是静态密钥协商算法,ECDH 的参数和公钥包含在证书中; 公钥必须使用一个能够被 Client 支持的曲线和点格式, 正如 [TLSECC] 中描述的那样。目前这种套件已经很少见了,因为 ECDH 不支持前向安全性 |
ECDHE_ECDSA | 证书包含 ECDSA-capable 公钥,使用 ECDHE 算法协商预备主密钥; 证书必须允许密钥用于使用将在 Server 密钥交换消息中使用的散列算法进行签名;公钥必须使用一个能够被 Client 支持的曲线和点格式,Client 通过 Client Hello 消息中的 ec_point_formats 扩展指定支持的命名曲线,正如 [TLSECC] 中描述的那样。这是 TLS 1.2 中最安全,性能最高的密码套件。 |
如果 Client 提供了一个 “signature_algorithms” 扩展,则 Server 提供的所有证书必须由出现在这个扩展中的一个 hash/签名算法对进行签名。需要注意的是这意味着一个包含了一个签名算法密钥的证书应该被一个不同的签名算法签名(例如,RSA 密钥被 DSA 密钥签名)。这个与 TLS 1.1 不同,TLS 1.1 中要求算法是相同的。更进一步也说明了 DH_DSS,DH_RSA,ECDH_ECDSA,和 ECDH_RSA 套件的后半部分对应的公钥不会用来加密或者数字签名,没有存在的必要性,并且后半部分也不限制 CA 机构签发证书所选用的数字签名算法。固定的 DH 证书可以被出现在扩展中的任意 hash/签名算法对签名。DH_DSS,DH_RSA,ECDH_ECDSA,和 ECDH_RSA 是历史上的名称。
如果 Server 有多个证书, 它基于上述标准(此外其它的标准有:传输层端点,本地配置和偏好等)选择其中一个。如果 Server 只有一个证书,它应该尝试使这个证书符合这些标准。
需要注意的是有很多证书使用无法与 TLS 兼容的算法或算法组合。例如,一个使用 RSASSA-PSS 签名密钥的证书(在 SubjectPublicKeyInfo 中是 id-RSASSA-PSS OID)不能被使用因为 TLS 没有定义相应的签名算法。
正如密钥套件指定了用于 TLS 协议的新的密钥交换方法,它们也同样指定了证书格式和要求的编码的按键信息。
至此已经涉及到了 Client 签名算法、证书签名算法、密码套件、Server 公钥,这 4 者相互有关联,也有没有关系的。
- Client 签名算法需要和证书签名算法相互匹配,如果 Client Hello 中的 signature_algorithms 扩展与证书链中的证书签名算法不匹配的话,结果是握手失败。
- Server 公钥与证书签名算法无任何关系。证书中包含 Server 证书,证书签名算法对 Server 公钥进行签名,但是 Server 公钥的加密算法可以是 RSA 也可以是 ECDSA。
- 密码套件和 Server 公钥存在相互匹配的关系,因为密码套件中的身份验证算法指的就是 Server 公钥类型。
TLS 1.2 会话恢复
当 Client 和 Server 决定复用之前的一个TLS连接会话时(走TLS握手缓存),需要以下流程:
Client 使用需要恢复的当前会话的 ID 发送一个 ClientHello。Server 检查它的会话缓存以进行匹配。如果匹配成功,并且 Server 愿意在指定的会话状态下重建连接,它将会发送一个带有相同会话 ID 值的 ServerHello 消息。这时,Client 和 Server 必须都发送 ChangeCipherSpec 消息并且直接发送 Finished 消息。一旦重建立完成,Client 和 Server 可以开始交换应用层数据(见下面的流程图)。如果一个会话 ID 不匹配,Server 会产生一个新的会话 ID,然后 TLS Client 和 Server 需要进行一次完整的握手。
Session ID
此时有两种方法可以恢复原来的session
- session ID
- session ticket
Session用于保存每个用户的专用信息,变量的值保存在服务器端,通过Session ID来区分不同的客户,通过这个Session ID,双方还可以重新使用已有的”对话密钥“,无需重新生成
- 基于 Session ID 的会话恢复
- Session ID 的来源:
- 上次完全握手生成的 Session ID
- 使用另外一条连接的 Session ID
- 直接使用本次连接的 Session ID
- 基于 Session ID 的会话恢复的优点是:
- 减少网络延迟,握手耗时从 2-RTT -> 1-RTT(client hello的时候就发了)
- 减少了 Client 和 Server 端的负载,减少了加密运算的 CPU 资源消耗
- 高兼容性
- 基于 Session ID 的会话恢复的缺点是:
- Server 存储会话信息,限制了 Server 的扩展能力。
- 分布式系统中,如果只是简单的在 Server 的内存中存储 Session Cache,那么多台机器的数据同步也是一个问题。(只能保留在一个服务器上)
- Session ID 的来源:
基于 Session ID 会话恢复的流程如下:
Client Server
ClientHello -------->
ServerHello
[ChangeCipherSpec]
<-------- Finished
[ChangeCipherSpec]
Finished -------->
Application Data <-------> Application Data
Session Ticket
基于 Session Ticket 的会话恢复流程:
对于 Server 来说,解密 ticket 就可以得到主密钥,(注意这里和 SessionID 不同,有 Session ID 可以得到主密钥的信息)
其思想是服务器取出它的所有会话数据(状态)并进行加密 (密钥只有服务器知道),再以票证的方式发回客户端。在接下来的连接中,客户端恢复会话时在 ClientHello 的扩展字段 session_ticket 中携带加密信息将票证提交回服务器,由服务器检查票证的完整性,解密其内容,再使用其中的信息恢复会话。
Session Ticket 的优点也就决定了它特别适合在以下场景中使用:
- 大型 HTTPS 网站,访问量非常大,在 Server 中存储 Session 信息需要消耗大量内存
- HTTPS 网站所有者希望会话信息的生命周期能足够长,让 Client 尽量都使用简短握手的方式
- HTTPS 网站所有者希望用户能跨地域跨主机访问
校验 SessionTicket 失败,那么握手会回退到完整握手。
如果 Client 在 ClientHello 中同时发送了 Session ID 和 SessionTicket TLS 扩展,SessionTicket 优先级高于 Session ID
SessionTicket 会话恢复
Client Server
ClientHello
(SessionTicket extension) -------->
ServerHello
(empty SessionTicket extension)
NewSessionTicket
[ChangeCipherSpec]
<-------- Finished
[ChangeCipherSpec]
Finished -------->
Application Data <-------> Application Data
Sever验证了客户端发送Finished消息后,通过”New session ticket”消息将session ticket发给客户端进行保存,服务器端就不再保存会话状态了,下图是这条消息所在的流程步骤:
客户端在下次发起会话时,就可以将Session Ticket放入client hello中,服务器收到后进行解密验证,并从ticket的内容中提取会话状态,用来恢复会话。
session ticket优点
- 可在其他服务器上使用,它原理上就是让xx的的请求一直粘连在机器A上,下次重连的时候,就把这个 Ticket 进行解密,验证它过没过期,如果没过期那就直接恢复之前的会话状态。
缺点:
兼容性比不上session id
和session id一样,有安全风险
- 之前会话的master key是否泄密(Session Ticket: 加密后的session status信息,包含了master key),否则新的session key也不安全。
- 缺乏身份验证机制,无法防止中间人攻击。(需要证书认证来保证)
- TLS给出的方案是,恢复会话必须双方都同意,如果有一方认为会话不安全,就都需要重新走一遍完整的握手流程;另外客户端保存session ticket的有效期最长不要超过24小时;
如果对于sessionID和ticket还不太明白
Session id:
Session id是由服务器分配的,发给客户端让客户端保存,在客户端发起新会话时,会将这个ID在client hello中发给服务器,用来判断是否恢复之前的session。
服务器在缓存中查找该id,如果找到了,并且服务器愿意重建该会话的话,那么就会在Sever Hello消息中发送同样的session id
之后双方就可以开始用之前协商过的加密模式开始传输数据。
Session id是最传统的方式,它的主要问题是,会话状态保存在服务器端,当大量用户访问服务器时,服务器用来保存会话状态的空间占用就会很大,需要对保存时间,保存机制做优化。
session ticket:
服务器在获取到客户端的密钥协商参数,并生成session key之后,会将完整的会话状态(session status)进行加密,生成session ticket,注意,这个信息只有服务器才能够解密
Sever验证了客户端发送Finished消息后,通过”New session ticket”消息将session ticket发给客户端进行保存,服务器端就不再保存会话状态了,下图是这条消息所在的流程步骤:
客户端在下次发起会话时,就可以将Session Ticket放入client hello中,服务器收到后进行解密验证,并从ticket的内容中提取会话状态,用来恢复会话。
(你大概可以理解为,把会话内容加密后当成session凭证来使用了)
PSK(pre_share_key)
在TLS1.3中,引入新的session resumption机制代替了session id和ticket.
双方经过握手和密钥协商,和身份验证之后,生成对称的密钥session key。
在收到客户端的finished消息之后,服务器通过”new session ticket”消息,携带PSK发送给客户端,注意这条消息是使用session key加密的,这就保证了只有通信双方才能读取里面的信息。(实质上我个人理解就是非对称密钥保证安全性,然后使用原来的对称密钥保证便捷性)
只要证明了双方都持有相同的 PSK,就不再需要证书认证来证明双方的身份,这样看来,PSK 也算是一种身份认证机制。
- 服务器可能会发送多个new session ticket,包含不同的PSK;
- 在建立新会话时,客户端将PSK信息放入client Hello消息中发送给服务器,报文如下:
- 如果服务器验证了该PSK的真实性,则会在server hello中的
pre_shared_key
中将加密的PSK发送给客户端; - 那么之前的PSK就会被用来生成新的session key,并开始加密传输数据,不需要再发送CA证书等验证消息了。
- 只要证明了双方都持有相同的PSK,不再需要证书认证,就可以证明双方的身份,因此,PSK也是一种身份认证机制。
Client Server
ClientHello
+ key_share -------->
ServerHello
+ key_share
{EncryptedExtensions}
{CertificateRequest*}
{Certificate*}
{CertificateVerify*}
{Finished}
<-------- [Application Data*]
{Certificate*}
{CertificateVerify*}
{Finished} -------->
<-------- [NewSessionTicket]
[Application Data] <-------> [Application Data]
TLS1.3
先简述一下 TLS 1.3 的一些优化和改进:
- 减少握手等待时间,将握手时间从 2-RTT 降低到 1-RTT,并且增加 0-RTT 模式。
- 删除 RSA 密钥协商方式,静态的 Diffie-Hellman 密码套件也被删除了。因为 RSA 不支持前向加密性。TLS 1.3 只支持 (EC)DHE 的密钥协商算法。删除了 RSA 的方式以后,能有效预防心脏出血的攻击。所有基于公钥的密钥交换算法现在都能提供前向安全。TLS 1.3 规范中只支持 5 种密钥套件,TLS13-AES-256-GCM-SHA384、TLS13-CHACHA20-POLY1305-SHA256、TLS13-AES-128-GCM-SHA256、TLS13-AES-128-CCM-8-SHA256、TLS13-AES-128-CCM-SHA256,隐藏了非对称加密密钥协商算法,因为默认都是椭圆曲线密钥协商。
- 删除对称加密中,分组加密和 MAC 导致的一些隐患。在 TLS1.3 之前的版本中,选择的是 MAC-then-Encrypt 方式。但是这种方式带来了一些漏洞,例如 BEAST,一系列填充 oracle 漏洞(Lucky 13 和 Lucky Microseconds)。CBC 模式和填充之间的交互也是 SSLv3 和一些 TLS 实现中广泛宣传的 POODLE 漏洞原因。在 TLS 1.3 中,已移除所有有安全隐患的密码和密码模式。你不能再使用 CBC 模式密码或不安全的流式密码,如 RC4 。TLS 1.3 中允许的唯一类型的对称加密是一种称为 AEAD(authenticated encryption with additional data)的新结构,它将加密性和完整性整合到一个无缝操作中。
- 在 TLS 1.3中,删除了 PKCS#1 v1.5 的支持,而选择更新的设计 RSA-PSS,提高了安全性。认证方面通过非对称算法,例如,RSA, 椭圆曲线数字签名算法(ECDSA),或 Edwards 曲线数字签名算法(EdDSA)完成,或通过一个对称的预共享密钥(PSK)。
- 在 TLS 1.2 的握手流程中,只有 ChangeCipherSpec 之后的消息会被加密,如 Finished 消息和 NewSessionTicket,其他的握手子消息不会加密。TLS 1.3 针对这个问题,对握手中大部分子消息全部进行加密处理。这样可以有效的预防 FREAK,LogJam 和 CurveSwap 这些降级攻击(降级攻击是中间人利用协商,强制使通信双方使用能被支持的最低强度的加密算法,从而暴力攻击计算出密钥,允许攻击者在握手时伪造 MAC)。在TLS 1.3中,这种类型的降级攻击是不可能的,因为服务器现在签署了整个握手,包括密码协商。
- TLS 1.3 完全禁止重协商。
- 密钥导出函数被重新设计,由 TLS 1.2 的 PRF 算法改为更加安全的 HKDF 算法。
- 废除 Session ID 和 Session Ticket 会话恢复方式,统一通过 PSK 的方式进行会话恢复,并在 NewSessionTicket 消息中添加过期时间和用于混淆时间的偏移值。
我个人归纳为
- 减少握手等待时间,将握手时间从 2-RTT 降低到 1-RTT,并且增加 0-RTT 模式。
- 删除 RSA 密钥协商方式,预防心脏出血的攻击;删除对称加密中,分组加密和 MAC 导致的一些隐患;删除了 PKCS#1 v1.5 的支持,而选择更新的设计 RSA-PSS,提高了安全性;对握手中大部分子消息全部进行加密处理。这样可以有效的预防 FREAK,LogJam 和 CurveSwap 这些降级攻击; 完全禁止重协商;重新设计密钥导出函数;总体来说都是提升安全性
- 废除 Session ID 和 Session Ticket 会话恢复方式,统一通过 PSK 的方式进行会话恢复,并在 NewSessionTicket 消息中添加过期时间和用于混淆时间的偏移值。
- 更加安全
4 种密钥协商
在 TLS 1.3 中,存在 4 种密钥协商的方法:
- Client 支持的加密套件列表。密码套件里面中能体现出 Client 支持的 AEAD 算法或者 HKDF 哈希对。(TLS1.2已有)
- “supported_groups” 的扩展 和 “key_share“ 扩展。“supported_groups” 这个扩展表明了 Client 支持的 (EC)DHE groups,”key_share” 扩展表明了 Client 是否包含了一些或者全部的(EC)DHE共享。(TLS1.3新增,完整握手)
- “signature_algorithms” 签名算法和 “signature_algorithms_cert” 签名证书算法的扩展。”signature_algorithms” 这个扩展展示了 Client 可以支持了签名算法有哪些。”signature_algorithms_cert” 这个扩展展示了具体证书的签名算法。(TLS1.3新增,比第二种常用)
- “pre_shared_key“ 预共享密钥和 “psk_key_exchange_modes” 扩展。预共享密钥扩展包含了 Client 可以识别的对称密钥标识。”psk_key_exchange_modes” 扩展表明了可能可以和 psk 一起使用的密钥交换模式。(TLS1.3新增,PSK,0-RTT)
初次握手降低为1RTT
TLS 1.3 之所以能比 TLS 1.2 完整握手减少 1-RTT 的原因就在 ClientHello 中就已经包含了 (EC)DHE 所需要的密钥参数,
(是的,我有了个key_share,但是实际上我个人感觉只是减少了0.5RTT,只不过将剩下finish所需要的的0.5RTT应该是放到application Data exchange中了)
不需要像 TLS 1.2 中额外用第二次 RTT 来进行 DH 协商参数。在 TLS 1.3 的 ClientHello 的 Extension 中,带有 key_share 扩展,这个扩展中包含了 Client 所能支持的 (EC)DHE 算法的密钥参数。并且 Extension 中还会有 supported_groups 扩展,这个扩展表明了 Client 支持的用于密钥交换的命名组。按照优先级从高到低。
Client Server
ClientHello
+ key_share -------->
ServerHello
+ key_share
{EncryptedExtensions}
{CertificateRequest*}
{Certificate*}
{CertificateVerify*}
{Finished}
<-------- [Application Data*]
{Certificate*}
{CertificateVerify*}
{Finished} -------->
<-------- [NewSessionTicket]
[Application Data] <-------> [Application Data]
条件:在 ServerHello 的 Extension 中必须要有的这 2 个扩展,supported_versions、key_share(如果是 PSK 会话恢复方式,还必须包含 pre_shared_key)。
supported_versions 是 TLS 1.3 中必带的扩展,如果没有这个扩展,Server 会认为 Client 只能支持 TLS 1.2,于是接下来的握手会进行 TLS 1.2 的握手流程。
TLS1.3会话恢复
TLS 1.3 在宣传的时候就以 0-RTT 为主,大家都会认为 TLS 1.3 再第二次握手的时候都是 0-RTT 的,包括网上一些分析的文章里面提到的最新的 PSK 密钥协商,PSK 密钥协商并非是 0-RTT 的。
TLS 1.3 再次握手其实是分两种:会话恢复模式、0-RTT 模式。非 0-RTT 的会话恢复模式和 TLS 1.2 在耗时上没有提升,都是 1-RTT,只不过比 TLS 1.2 更加安全了。只有在 0-RTT 的会话恢复模式下,TLS 1.3 才比 TLS 1.2 有提升。具体提升对比见下表:
HTTP/2 + TLS 1.2 首次连接 | HTTP/2 + TLS 1.2 会话恢复 | HTTP/2 + TLS 1.3 首次连接 | HTTP/2 + TLS 1.3 会话恢复 | HTTP/2 + TLS 1.3 0-RTT | |
---|---|---|---|---|---|
DNS 解析 | 1-RTT | 0-RTT | 1-RTT | 0-RTT | 0-RTT |
TCP 握手 | 1-RTT | 1-RTT | 1-RTT | 1-RTT | 1-RTT |
TLS 握手 | 2-RTT | 1-RTT | 1-RTT | 1-RTT | 0-RTT |
HTTP 请求 | 1-RTT | 1-RTT | 1-RTT | 1-RTT | 1-RTT |
总计 | 5-RTT | 3-RTT | 4-RTT | 3-RTT | 2-RTT |
Client Server
Subsequent Handshake:
ClientHello
+ key_share*
+ pre_shared_key -------->
ServerHello
+ pre_shared_key
+ key_share*
{EncryptedExtensions}
{Finished}
<-------- [Application Data*]
{Finished} -------->
[Application Data] <-------> [Application Data]
0RTT模式
TLS 1.3 废除了之前的 Session ID 和 Session Ticket 的会话恢复的方式,统一成了 PSK 方式,使得原有会话恢复变的更加安全。但是 TLS 1.3 的会话恢复并没有降低 RTT,依旧停留在了 1-RTT。为了进一步降低延迟,于是提出了 0-RTT 的概念。0-RTT 能让用户有更快更顺滑更好的用户体验,在移动网络上更加明显。
TLS 1.3 的里程碑标志就是添加了 0-RTT 会话恢复模式。也就是说,当 client 和 server 共享一个 PSK(从外部获得或通过一个以前的握手获得)时,TLS 1.3 允许 client 在第一个发送出去的消息中携带数据(”early data”)。Client 使用这个 PSK 生成 client_early_traffic_secret 并用它加密 early data。Server 收到这个 ClientHello 之后,用 ClientHello 扩展中的 PSK 导出 client_early_traffic_secret 并用它解密 early data。
0-RTT 会话恢复模式如下:
Client Server
ClientHello
+ early_data
+ key_share*
+ psk_key_exchange_modes
+ pre_shared_key
(Application Data*) -------->
ServerHello
+ pre_shared_key
+ key_share*
{EncryptedExtensions}
+ early_data*
{Finished}
<-------- [Application Data*]
(EndOfEarlyData)
{Finished} -------->
[Application Data] <-------> [Application Data]
可以理解为此时为TLS1.3握手恢复,我们客户端服务端双方认证 + 携带PSK,我们客户端发送第一次请求时,直接携带PSK + 通过PSK加密的要发送的数据内容(early data)
想实现 0-RTT 也是有一些条件的,条件比较苛刻,如果条件有一条不满足,会话恢复都只能是 1-RTT 的 PSK 会话恢复模式。
0-RTT 的开启条件是:
- Server 在前一次完整握手中,发送了 NewSessionTicket,并且 Session ticket 中存在max_early_data_size 扩展表示愿意接受 early data。如果没有这个扩展,0-RTT 无法开启。
- 在 PSK 会话恢复的过程中,ClientHello 的扩展中配置了 early data 扩展,表示 Client 想要开启 0-RTT 模式。
- Server 在 Encrypted Extensions 消息中携带了 early data 扩展表示同意读取 early data。0-RTT 模式开启成功。
只有同时满足了上面 3 个条件,才能开启 0-RTT 会话恢复模式。否则握手会是 1-RTT 的会话恢复模式。
注意:目前不少浏览器虽然支持 TLS 1.3 协议,但是还不支持发送 early data,所以它们也没法启用 0-RTT 模式的会话恢复。
而Server 想拒绝 Client 的 0-RTT 会话恢复,只要打破 3 个开启条件即可,可以:
- 拒绝 PSK。Server 在 ServerHello 中不加入 pre_shared_key 扩展,那么握手就会回退到完整握手,自然拒绝了 0-RTT。
- 只拒绝 early_data,接受 PSK。在 ServerHello 中,加入 pre_shared_key 扩展,但是EncryptedExtension 子消息中不加入 early_data 扩展。
0-RTT 存在安全性风险
但是 0-RTT 存在安全性风险。0-RTT 数据安全性比其他类型的 TLS 数据要弱一些,特别是:
- 0-RTT 的数据是没有前向安全性的,它使用的是被提供的 PSK 中导出的密钥进行加密的。
- 在多个连接之间不能保证不存在重放攻击。普通的 TLS 1.3 1-RTT 数据为了防止重放攻击的保护方法是使用 server 下发的随机数,现在 0-RTT 不依赖于 ServerHello 消息,因此保护措施更差。如果数据与 TLS client 认证或与应用协议里一起验证,这一点安全性的考虑尤其重要。这个警告适用于任何使用 early_exporter_master_secret 的情况。(不依赖server hello了,所以没有随机数保证重放攻击的防御)
TLS 1.3 0-RTT 中要预防重放攻击。预防 0-RTT 有 4 种措施:
- 第一个措施检查 PSK 中的过期时间,如果过期了,就不处理 early_data 中的请求,并且将握手降级到 1-RTT。
- 第二个措施是不允许非幂等性的请求出现在 0-RTT 中,如果出现了非幂等性的请求,Server 将会忽略不处理,GET 请求是幂等性的,但是也不能允许后面带参数,不带参数的 GET 请求才能允许。
- 第三个措施是,在请求头中记录 PSK binder 的值或者一个随机值,这个值能保证 0-RTT 的 early_data 全局唯一,这样就可以防止重放攻击。当收到 ClientHello 时,Server 首先验证 PSK binder。然后它会计算 expected_arrival_time,如果它在记录窗口之外,则拒绝 0-RTT,然后回到 1-RTT 握手。如果 expected_arrival_time 在窗口中,则 Server 检查它是否记录了匹配的 ClientHello。如果找到一个,它将使用 “illegal_parameter” alert 消息中止握手或接受 PSK 但拒绝 0-RTT。如果找不到匹配的 ClientHello,则它接受 0-RTT,然后只要 expected_arrival_time 在窗口内,就存储 ClientHello。Server 也可以实现具有误报的数据存储,例如布隆过滤器,在这种情况下,它们必须通过拒绝 0-RTT 来响应明显的重放,但绝不能中止握手。关于这一个措施,还可能存在多个 binder 的情况,如果是分布式系统,还会存在多个 zone 的问题,具体分析见笔者这篇文章 《TLS 1.3 0-RTT and Anti-Replay》。
- 第四个措施是,在数据库里面记录所有未完成有效的 ticket,使用一次就删除掉,如果产生重放攻击,那么这个 ticket 必然是数据库里面查不到的,那么就回退到完整握手。
参考:
HTTPS 温故知新(三) —— 直观感受 TLS 握手流程(上)