HTTP 安全通信保障:TLS、身份验证、授权

2023-11-22 09:51:49 浏览数 (3)

对于交易支付相关的网络服务,需要保障安全、私密的网络通信。此时需要依赖 TLS 、身份验证和授权机制。我们先来了解基本概念。

TLS

TLS(Transfer Layer Security) 指传输层安全协议,它包括记录协议(例如 TCP )和握手协议,在下文中仅关注握手协议。

它的一种常见用法是和 HTTP 结合使用成为 HTTPS 。 HTTPS 的 S 还可以指代 SSL(Secure Sockets Layer),它是 TLS 的前身,目前也会用 SSL 指代 TLS。

那么,TLS 是如何在不可信的网络环境中实现安全地通信的呢?

首先,在建立连接的过程(即握手),完成密钥协商和身份验证。连接建立后,在每次请求中,使用密钥对数据加密来保证数据的保密性;使用签名和验签保证数据的完整性。

TLS 协议有1.1、1.2、1.3,当前使用的主流是 1.2。我们推荐使用最新的 TLS 1.3,它修复了 1.2 中的安全漏洞问题。

下面我们将从 TLS 1.1 开始了解整个 TLS 的演变过程。

TLS 1.1

下图是完整的握手流程:

* 表示的消息是可选发送的。

第一步:Client 向 Server 发送 ClientHello 消息。消息包括生成密钥的随机数1。 第二步:Server 发送 ServerHello 消息。消息包括:

  • 生成密钥的随机数2。
  • Certificate(服务端证书):包含签名算法、公钥等信息。
  • ServerKeyExchange(服务端密钥交换信息):包括密钥交换算法(RSA 或 DH ),仅当服务端证书信息不足以让客户端完成密钥协商时,才会补充发送。
  • CertificateRequest(证书请求):当服务端需要客户端提供证书时,发送此消息。包括消息可供选择的算法等。

第三步:Server 发送 SeverHelloDone 消息。消息包括 ServerHello 完成的关联数据。 第四步:Client 收到 ServerHelloDone 消息,验证服务端证书有效性。然后发送以下消息内容:

  • Certificate(客户端证书):仅当服务端发送证书请求消息时发送。
  • ClientKeyExchange(客户端密钥交换信息):生成密钥的最后一部分信息。如果密钥交换算法为 RSA ,则为使用服务端证书中的公钥加密后的随机数3;如果为 DH 算法则为 DH 参数。
  • CertificateVerify(客户端证书验证):消息为使用客户端证书对应的私钥签名。仅当发送了客户端证书时发送,以供服务端验证客户端证书。
  • 完成:客户端在更改了密钥规范信息后,此时客户端已具备生成密钥的算法和生成信息,生成密钥。 然后使用密钥加密并发送完成消息。

第五步:服务端接收到第四步消息,客户端身份和消息验证通过,生成密钥并使用密钥加密并发送完成消息。

以上就是 TLS 握手的完整流程。简单来说,就是客户端和服务器交换随机数完成密钥协商,通过签名和验签完成身份认证。

TLS 1.2

TLS 1.2 的握手流程和 TLS 1.1 完全一致:

相对 TLS 1.1,TLS 1.2 主要收紧限制了密钥协商中使用的算法,使用更新的安全算法。例如使用 SHA256 替换 MD5/SHA1。

TLS 1.3

TLS 1.3 是一个不向下兼容的协议,它极大地提高了通信安全和简化流程,是我们了解的重点。它的主要改进点如下:

  • 已支持的对称加密算法中,仅保留 AEAD 类型的算法;更改密码套件概念
  • 添加 0-TTR 模式
  • 提供向前保密
  • ServerHello 后的消息全部加密

下图是完整的握手流程:

握手流程简化成了3步:KeyExch(密钥交换)、Server Params(生成服务器参数)、Auth(身份验证)。

这三步中有很多可选参数,根据密钥交换模式不同选择传输。密钥交换支持 (EC)DHE、PSK-only、PSK with (EC)DHE。我们先来看这三种模式的含义。

密钥交换模式为 (EC)DHE

DH(Diffie–Hellman key exchange),即 Diffie–Hellman 密钥交换,是一种在公共通道中安全交换密钥的数学方法。假设通信为 a、b,基于 DH 生成密钥的流程如下:

  1. a、b 约定参数 p、q(用于生成g)、g。
  2. a、b 分别生成随机数作为私钥 xa、xb。
  3. a 根据 g ^ xa mod p 生成公钥 ya,b 根据 g ^ xb mod p 生成公钥 yb。
  4. a 根据 (yb ^ xa) mod p 生成统一变量 zz,b 根据 (ya ^ xb) mod p 生成统一变量 zz。
  5. 双方分别得到 zz,根据约定算法计算得到密钥。

整个过程通过自己的私钥,对方的公钥以及 p、q、g 参数、约定的密钥计算算法,就可以生成密钥。第三方需要计算得到私钥很困难,需要解决离散对数问题。

DHE(Diffie-Hellman Ephemeral)是临时 DHE,临时指的是公私钥对,每一次使用双方都会重新生成公私钥对,因此每一次都能计算得到不同的密钥。

这样的好处是每个连接都能生成不同密钥,当前连接的密钥泄漏,不会影响在此之前建立的连接。这个特性就叫 FS(Forward Secrecy)前向保密

EC(Elliptic Curve)指基于椭圆曲线,椭圆曲线密码学是一种公钥计算方法,它把公私钥作为椭圆上的一个点,指定私钥和椭圆曲线参数能计算得到公钥。使用它的好处是能提高密钥生成效率和加强安全性。

因此,(EC)DHE 的含义为 (基于椭圆曲线密码学得到公私钥的)临时 DH 算法。EC 是可选的。

密钥交换模式为 PSK-only

PSK(Pre-shared key)即预共享密钥,它可以在连接建立之前通过某种安全通道交换,例如离线部署;也可以在新的连接中使用上一次连接中设定的 PSK ID 来恢复使用上一次连接的 PSK。

PSK-only 即指只使用 PSK 的密钥交换模式,密钥只基于 PSK 生成。

使用它的好处是能实现 零往返时间(0-RRT) 模式。基于 PSK 建立的连接,客户端可以在 ClientHello 中就发送数据。同时使用 PSK 加密数据,验证服务端身份是否合法。

虽然这种模式可以节省时间,但是有一定的安全限制。

密钥交换模式为 PSK with (EC)DHE

PSK with (EC)DHE 即结合 (EC)DHE 的 PSK 。PSK 和 (EC)DHE 的 zz 参数一起作为参数生成密钥。这样做的好处是,相比 PSK-only,能够保证前向安全。

握手流程

现在来看握手流程。

第一步,Client 向 Server 发送 ClientHello 消息。包括:

  • 密钥共享(key_share):若密钥交换模式包含 (EC)DHE,则包括客户端的 (EC)DHE 使用的参数信息,例如 p、q、g,公钥。客户端也可以发送空内容,让服务端选择参数。
  • 签名算法(signature_algorithms):在 TLS 1.3 中,仅允许 RSA、ECDSA、EdDSA 算法。
  • PSK 交换模式(psk_key_exchange_modes):密钥交换模式为 PSK-only 时,值为 psk_key;密钥交换模式为 PSK with(EC)DHE 时,值为 psk_dhe_key
  • PSK

第二步,Server 向 Client 发送 ServerHello 消息。包括:

  • 密钥共享(key_share):服务端的 (EC)DHE 使用的参数。例如公钥。
  • PSK
  • 加密扩展(EncryptedExtensions):对不需要确定加密参数的 ClientHello 扩展的响应。这是使用 server_handshake_traffic_secret 加密的数据。
  • 证书请求(CertificateRequest):当服务端需要客户端提供证书时,发送此消息。包括消息可供选择的算法等。
  • 服务端证书(Certificate)
  • 服务端证书验证(CertificateVerify):使用服务端证书对应的私钥签名。以供客户端验服务端证书。
  • 完成(Finished)消息:该消息为一个 MAC, server_handshake_traffic_secret 作为密钥,使用 HMAC 计算得出。客户端需要校验该消息是否合法。
  • 应用消息(Application Data):请求的应用消息。

第三步,客户端验证服务端的身份。它会发送如下消息:

  • 客户端证书(Certificate):当服务端请求客户端提供证书时发送。
  • 客户端证书验证消息(CertificateVerify):使用客户端证书对应的私钥签名。以供服务端验证客户端证书。
  • 完成(Finished)消息:该消息为一个 MAC, client_handshake_traffic_secret 作为密钥,使用 HMAC 计算得出。服务端需要校验该消息是否合法。
  • 应用消息(Application Data):请求的应用消息。

至此,握手完成。握手流程中出现了一些密钥,用于加密数据或者生成 MAC。这也是 TLS 1.3 的一个特性:将身份验证、密钥交换机制中加密和生成 hash 值的密钥分开,以获得更高的安全性。

我们来了解下这些密钥是如何生成的。

密钥生成机制和方法

TLS 1.2 使用的是 KDF(Key Derivation Function) 密钥派生函数来得到密钥。它通过使用伪随机函数来获取所需格式的密钥。

TLS 1.3 使用的是 HKDF(HMAC KDF)[RFC5869],即基于 HMAC 的 KDF 算法。它的原理是先提取再拓展。先采用输入的密钥生成材料并从中提取固定长度的伪随机密钥,然后将伪随机密钥拓展为几个额外的伪随机密钥。在 TLS 1.3 中,这些伪随机密钥就被用于身份验证、加密等不同用途。

HKDF 的两个步骤分别为 HKDF-Extract 和 Derive-Secret。

HKDF-Extract 的 计算方法如下图:

代码语言:javascript复制
   HKDF-Extract(salt, IKM) -> PRK
   PRK = HMAC-Hash(salt, IKM)

salt 为可选参数,使用盐可以提高 HKDF 算法的强度。 IKM(input keying material)为输入密钥材料,它可以为空输出 PKR(伪随机密钥)。

Derive-Secret 的计算方法如下图:

它的输入为 Secret(密钥)、Label。

下图是 TLS 1.3 的密钥派生体系:

  • HKDF-Extract 从上方获取 salt,从左边获取 IKM,输出内容在中间,输出名称在右边。
  • Derive-Secret 的 Secret 参数由传入箭头指示。例如,Early Secret 是用于生成 client_early_traffic_secret 的 Secret。
  • 0 表示输入长度为0的参数。

Derive的输入原材料为 PSK 和 (EC)DHE 中生成的 ZZ。

第一步, PSK 作为输入密钥, 和长度为0的盐通过 HKDF-Extract 生成 Early Secret。

第二步,将 Early Secret 和固定字段、ClientHello 结合,通过 Derive-Secret 方法计算得到 binder_key、client_early_traffic_secret、early_exporter_master_secret。

第三步, (EC)DHE 的 ZZ 作为输入密钥,和 Derive-Secret(Early Secret, "derived", "") 的计算值作为盐,通过 HKDF-Extract 计算得到 Handshake Secret。Handshake Secret 和固定字段、ClientHello、ServerHello 结合,通过 Derive-Secret 方法计算得到 client_handshake_traffic_secret、server_handshake_traffic_secret。

第四步,Derive-Secret(Handshake Secret, "derived", "") 作为盐通过 HKDF-Extract 得到 Master Secret。Master Secret 和固定字段、ClientHello 、server Finished 等得到4个 Secret。

上述过程用于握手和通信的密钥如下:

client_early_traffic_secret 加密 0-RTT 的数据; sender_handshake_traffic_secret 加密握手过程的数据; sender_application_traffic_secret_N 加密通信过程的应用数据。

以上是 TLS 1.3 的大概流程。

TLS 能够保障建立安全隐私的网络通道。基于 TLS,客户端和服务器需要通过身份验证来授权,从而完成获取资源。我们先来看身份验证和授权的方式。

身份验证

客户端在请求服务器获取资源前,第一步是证明自己的身份,服务端再根据客户端的身份授权。证明自己的身份,即身份验证(Authentification)

身份验证大部分是单向的,由服务端直接或依赖第三方来验证客户端的身份。基于互不信任原则,也可以使用双向验证,即客户端和服务端互相验证。

在 HTTP 请求中,使用凭据验证身份。凭据可以是静态或动态生成的,它随着每次请求传输。常见的凭据中,静态的包括用户密码、API 密钥等;动态的包括数字签名。

  • 用户密码:最不安全的一种凭据,一般不会使用这种方式。凭据被窃取即意味着用户信息被窃取。
  • API 密钥:较为常见的身份验证凭据。这是服务端提供与客户端唯一对应的 API 密钥。
  • 数字签名:基于非对称密钥体系,使用私钥生成签名,公钥验证签名。

从不可抵赖性以及被窃取后可能造成的严重程度来看,凭据选择的优先级为数字签名 > API 密钥 > 用户密码。

授权

授权(Authorization)是指向经过身份验证的参与方授予执行某项操作的权限的操作。 授权的核心是授权凭据。

服务端可以直接或依赖第三方来授权客户端。直接授权使用的凭据和身份验证凭据一样,它结合内部授权机制实现。第三方授权通过第三方授权服务器获取授权凭据,主要依赖于采用 OAuth 2.0 标准的第三方实现。

OAuth 2.0

OAuth 2.0 [RFC6749] 是一个行业标准的授权协议。我们需要了解4个角色、2个资源、3个流程。

4个角色为Client(客户端)、 Resource Owner(资源拥有者)、Authorization Server(授权服务器)、Resource Server(资源服务器)。

2个资源分别为 Authorization Grant(授权授予)、Access Token(访问令牌)。

3个流程如下图:

第一步,客户端先向资源拥有者发送授权请求,获取授权授予。

第二步,客户端向授权服务器发送授权授予,获取资源的访问令牌。

第三步,客户端向资源服务器发送访问令牌,获取受保护的资源。

OAuth 2.0 中的访问令牌就是授权凭据。获取访问令牌有4种方式:

  • 授权码方式:通过授权码请求授权服务器获取令牌。
  • (授权码)隐藏式:无需授权码,客户端请求授权服务器获取令牌给前端。适用于请求服务没有后端服务的情景。
  • 密码式:通过用户密码请求授权服务器获取令牌。
  • 凭证式:通过 client_id 和 client_secret 请求授权服务器获取令牌,适用于命令行场景。

出于安全考虑,推荐使用授权码和凭据式。

HTTP 身份验证和授权方案

HTTP 提供了一个主流的身份验证和授权框架 [rfc7235],它的主要流程如下(图来自 HTTP authentication):

客户端需要在 HTTP 头部 Authorization 中放入凭据。若凭据无效,服务器会返回 401 Unauthorized;若授权凭据有效但权限不足以访问给定资源,服务器会返回 403 Forbidden。

凭据有几种类型,上面的 Authorization 头中的 Basic 就指基础类型,对凭据 Base64 编码。其他的类型还有:

  • Bearer: 基于 OAuth 2.0,表示 OAuth 2.0 的令牌。
  • Digest:Basic的增强版,对凭据使用 SHA-256 或 MD5 生成 HASH 值。
  • HOBA:HTTP Origin-Bound Authentication,凭据为共享密钥签出的数字签名。
  • Mutual:基于双向认证,凭据为分别计算得到的共同密钥计算得到的数字签名。
  • AWS4-HMAC-SHA256:AWS 服务的特定类型。

在此方案中,身份验证和授权都是依靠同一个凭据完成的,该凭据被放在 Authorization 。在其他实现中,也可以放在包体或者 URL。**出于安全考虑,推荐凭据放在 Authorization 头部或者包体。 **

实现

了解了身份验证和授权的基本概念和方式,我们来看支付网关实际是怎么做的。

国外主流支付网关的做法是基于 OAuth 2.0 来验证身份和授权,国内主流支付网关的做法是基于数字签名来验证身份和授权。

基于 OAuth 2.0 和 HTTP 身份验证授权框架

PayPal 就是基于 OAuth 2.0 和 HTTP 身份验证授权框架实现的典型例子。

PayPal 使用凭据式获取 access token 。获取 token 后,在后续请求中,结合 HTTP 身份验证框架,将 token 设置在 Authorization 头,向资源服务器请求。

代码语言:javascript复制
graph LR
    A[Client] -->| client ID: Client Sercret | B(Authorization Server)
	    B -->| access token | A	
代码语言:javascript复制
graph LR
A[Client] --> | Authorization: Bearer access token | B(Resource Server)

基于数字签名

微信支付和支付宝都是基于数字签名实现身份验证和授权的。

以微信支付为例:

商户在微信支付的商户平台获取商户公钥、私钥、微信支付平台证书,在向微信支付请求时,使用商户私钥对请求按照特定规则签名,并放在 Authorization 头中。微信支付返回时,会使用微信支付平台私钥对返回签名,并放在返回的 Authorization 中。

数字签名除了有身份验证的能力,还能保证消息的完整性。一旦消息被篡改了,计算得到的签名和消息体就会对应不上。

代码语言:javascript复制
graph LR
A[商户] --> | Authorization: 认证类型 签名 | B(微信支付)
B[微信支付] --> | Authorization: 认证类型 签名 | A(商户)

参考文档

wiki

  • PRF
  • AEAD
  • Elliptic-curve Diffie–Hellman
  • Diffie-Hellman Ephemeral
  • HKDF
  • Probabilistic signature scheme

rfc

  • The Transport Layer Security (TLS) Protocol
  • The Transport Layer Security (TLS) Protocol Version 1.2
  • The Transport Layer Security (TLS) Protocol Version 1.3
  • Pre-Shared Key Ciphersuites for Transport Layer Security (TLS)
  • Negotiated Finite Field Diffie-Hellman Ephemeral Parameters for Transport Layer Security (TLS)
  • Diffie-Hellman Key Agreement Method
  • HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
  • HMAC
  • Hypertext Transfer Protocol (HTTP/1.1): Authentication
  • The 'Basic' HTTP Authentication Scheme
  • The OAuth 2.0 Authorization Framework: Bearer Token Usage

其他

  • TLS中的密钥计算

1 人点赞