微服务安全基础:JWT、TLS 与 OAuth 2.0 详解
1. JWT 声明集与签名
JWT 声明集是一个 JSON 对象,其成员是 JWT 发行者所声明的内容。JWT 中的每个声明名称必须唯一,若存在重复的声明名称,JWT 解析器要么返回解析错误,要么只返回最后一个重复声明的声明集。JWT 规范并未明确规定哪些声明是必需的,哪些是可选的,这取决于每个使用 JWT 的应用程序自行定义。例如,OpenID Connect 规范定义了必需和可选的声明,其中
iss
、
sub
、
add
、
exp
和
iat
被视为必需元素,而
auth_time
、
nonce
、
acr
、
amr
和
azp
是可选元素。除了规范中定义的必需和可选声明外,令牌发行者还可以在 JWT 声明集中包含额外的元素。
JWT 的第三部分是签名,同样采用 base64url 编码。与签名相关的加密元素在 JOSE 头部中定义。在这个特定示例中,发行的令牌使用 RSASSA - PKCS1 - V1_5 与 SHA - 256 哈希算法,这在 JOSE 头部的
alg
元素值中表示为
RS256
。签名是针对 JWS 中的前两部分(JOSE 头部和 JWT 声明集)计算得出的。
2. 信任传播与用户身份
用户上下文可以通过 JWS 在微服务之间传递。由于调用微服务使用已知的密钥对 JWS 进行签名,因此它既携带了最终用户的身份(如 JWT 中所声明的),也携带了调用微服务的身份(通过签名)。换句话说,调用微服务本身就是 JWS 的发行者。接收微服务要接受 JWS,首先需要根据嵌入在 JWS 中的公钥或通过其他机制获取的公钥来验证 JWS 的签名。仅仅这样还不够,还需要检查是否可以信任该密钥。微服务之间的信任可以通过多种方式建立:
-
提供可信证书
:按服务为每个微服务提供可信证书,但这种方法在微服务部署中难以扩展。
-
构建私有证书颁发机构(CA)
:建议构建一个私有 CA,并在需要时让不同的微服务团队使用中间证书颁发机构。这样,接收微服务只需信任根证书颁发机构或中间机构,从而大大减少证书供应的开销。
此外,信任引导是一个较难解决的问题,Secure Production Identity Framework For Everyone (SPIFFE) 项目为此构建了一个有趣的解决方案,可用于在微服务部署中的不同节点之间引导信任。使用 SPIFFE,每个节点将获得一个标识符和一对密钥,用于向与其通信的其他节点进行身份验证。
在 JWT 中,用于对令牌进行签名的密钥对应的公钥代表调用者(或调用微服务)。接收微服务通过 JWT 声明集中的
sub
参数来查找最终用户信息,该参数表示拥有 JWT 的主体或用户。如果任何微服务在其操作中需要识别用户,就应该查看这个属性。
sub
属性的值仅对于给定的发行者是唯一的。如果一个微服务接受来自多个发行者的令牌,那么用户的唯一性应由发行者和
sub
属性的组合来决定。除了主体标识符外,JWT 还可以携带用户属性,如
first_name
、
last_name
、
email
等。
当通过 JWT 在微服务之间传递用户上下文时,每个微服务都需要承担 JWT 验证的成本,其中包括验证令牌签名的加密操作。在微服务级别对从 JWT 中提取的数据进行缓存可以减少重复令牌验证的影响。缓存的过期时间必须与 JWT 的过期时间相匹配。如果 JWT 的过期时间很短,那么缓存的影响也会很小。
当令牌发行者发行 JWT 时,必须将其发行给特定的受众。受众是令牌的消费者。例如,如果微服务
foo
想与微服务
bar
通信,那么令牌由
foo
(或第三方发行者)发行,令牌的受众是
bar
。JWT 声明集中的
aud
参数指定了令牌的预期受众,它可以是单个接收者或一组接收者。在进行任何验证检查之前,令牌接收者必须首先查看该特定 JWT 是否是为其使用而发行的。如果不是,应立即拒绝。令牌发行者在发行令牌之前应该知道令牌的预期接收者是谁。
aud
参数的值必须是令牌发行者和接收者之间预先商定的值。在微服务环境中,可以使用正则表达式来验证令牌的受众。例如,令牌中的
aud
值可以是
*.facilelogin.com
,而
facilelogin.com
域下的每个接收者可以有自己的
aud
值,如
foo.facilelogin.com
、
bar.facilelogin.com
等。
3. 传输层安全(TLS)相互认证
传输层安全(TLS)相互认证,也称为客户端认证或双向安全套接字层(SSL),是 TLS 握手过程的一部分。在单向 TLS 中,只有服务器向客户端证明其身份,这在电子商务中常用于通过保证电子商务供应商的合法性来赢得消费者的信任。相比之下,相互认证对双方(客户端和服务器)进行身份验证。在微服务环境中,TLS 相互认证可用于微服务之间的相互身份验证。
TLS 相互认证和基于 JWT 的方法都要求每个微服务拥有自己的证书。两者的区别在于,在基于 JWT 的认证中,JWS 可以携带最终用户身份以及上游服务身份;而在 TLS 相互认证中,最终用户身份必须在应用层传递,可能作为 HTTP 头部。
4. 证书撤销
在 TLS 相互认证和基于 JWT 的方法中,证书撤销都是一个棘手的问题。虽然有多种可用的选项:
| 方法 | 描述 | 问题 |
| ---- | ---- | ---- |
| CRL(证书撤销列表 / RFC 2459) | 证书颁发机构(CA)必须维护一个撤销证书列表。发起 TLS 握手的客户端必须从相应的证书颁发机构获取撤销证书的长列表,然后检查服务器证书是否在撤销证书列表中。 | 客户端可以本地缓存 CRL,但会导致基于陈旧数据做出安全决策的问题。在 TLS 相互认证中,服务器也需要对客户端进行相同的证书验证。这种方法使用频率逐渐降低。 |
| OCSP(在线证书状态协议 / RFC 2560) | TLS 客户端可以在不下载证书颁发机构的整个撤销证书列表的情况下检查特定证书的状态。每次客户端与新的下游微服务通信时,都必须与相应的 OCSP 响应器通信以验证服务器(或服务)证书的状态,服务器也需要对客户端证书进行相同的操作。 | 会在 OCSP 响应器上产生大量流量,客户端仍然可以缓存 OCSP 决策,但同样会导致基于陈旧数据做出决策的问题。 |
| OCSP Stapling(RFC 6066) | 客户端在与下游微服务通信时无需每次都访问 OCSP 响应器。下游微服务会从相应的 OCSP 响应器获取 OCSP 响应,并将其附加到证书上。由于相应的证书颁发机构对 OCSP 响应进行了签名,客户端可以通过验证签名来接受它。 | 在相互 TLS 认证模型中,与普通 OCSP 相比,不会带来额外的好处。 |
| OCSP Stapling Required | 服务(下游微服务)向客户端(上游微服务)保证在 TLS 握手期间接收到的服务证书附有 OCSP 响应。如果证书未附有 OCSP 响应,客户端必须立即拒绝连接,而不是进行软失败。 | 无 |
5. 短期证书
从最终用户的角度来看,短期证书的行为与普通证书相同,只是有效期非常短。TLS 客户端无需担心对短期证书进行 CRL 或 OCSP 验证,只需关注证书上标注的过期时间。
短期证书的主要挑战在于其部署和维护。Netflix 建议使用分层方法来构建短期证书部署:
1. 拥有一个系统身份或长期凭证,存储在具有高度安全性的可信平台模块(TPM)或软件防护扩展(SGX)中。
2. 使用该凭证获取短期证书。
3. 将短期证书用于微服务,供其他微服务使用。每个微服务可以使用其长期凭证定期刷新短期证书。
仅仅拥有短期证书是不够的,承载服务(或 TLS 终止器)的底层平台应该支持对服务器证书进行动态更新。许多 TLS 终止器支持动态重新加载服务器证书,但在大多数情况下无法实现零停机。
下面是 Netflix 短期证书使用的 mermaid 流程图:
graph LR
A[系统身份/长期凭证(TPM/SGX)] --> B[获取短期证书]
B --> C[微服务使用短期证书]
C --> D[定期刷新短期证书]
D --> B
6. 边缘安全
将微服务暴露给外部世界的一种常见方法是使用 API 网关模式。需要暴露到外部的微服务在 API 网关中有相应的 API,但并非所有微服务都需要通过 API 网关暴露。最终用户通过 API 访问微服务的操作应该在边缘(即 API 网关)进行验证。最常见的 API 安全方法是 OAuth 2.0,随着时间的推移,OAuth 2.0 已成为 API 安全的事实上的标准。
7. OAuth 2.0
OAuth 2.0 是一个访问委托框架,允许一方代表另一方执行操作。OAuth 2.0 流程中有四个主要角色:客户端、授权服务器、资源服务器和资源所有者。例如,构建一个允许用户将其 Flickr 照片导出到其中的 Web 应用程序,该 Web 应用程序就是 OAuth 2.0 客户端,Flickr 是资源服务器(持有其用户的照片),想要将照片导出到 Web 应用程序的 Flickr 用户是资源所有者。为了让应用程序代表 Flickr 用户访问 Flickr API,需要某种授权许可,授权服务器会颁发这种授权许可,在这种情况下就是 Flickr 本身。但在实际应用中,授权服务器和资源服务器可能是两个不同的实体,OAuth 2.0 并未将它们绑定在一起。
OAuth 2.0 客户端可以是 Web 应用程序、原生移动应用程序、单页应用程序或桌面应用程序。每个 OAuth 客户端都有一个由授权服务器分配的标识符,称为客户端 ID。当客户端与授权服务器通信时,必须传递其客户端 ID。在某些情况下,客户端需要使用某种凭证来证明自己的身份,最常见的凭证形式是客户端密钥,类似于密码,但建议 OAuth 客户端使用更强大的凭证,如证书或 JWT。
OAuth 2.0 引入了多种授权类型,核心规范(RFC 6749)定义了五种授权类型:
-
授权码
:最流行的授权类型,超过 70% 的 Web 应用程序使用,适用于多种应用场景。
-
隐式
-
密码
-
客户端凭证
:当系统以自身身份访问 API 时使用。
-
刷新
想要通过 API 网关访问微服务的人必须首先获得有效的 OAuth 令牌。系统可以以自身身份或代表其他用户访问微服务。当系统代表其他用户访问 API 时,推荐使用授权码授权类型;在其他情况下,系统以自身身份访问 API 时,可以使用客户端凭证授权类型。
OAuth 2.0 有两种类型的访问令牌:
-
引用令牌
:授权服务器颁发给客户端应用程序的任意字符串,用于访问资源服务器。资源服务器看到引用访问令牌时,必须与相应的授权服务器通信以进行验证。
-
自包含令牌
:签名的 JWT(或 JWS)。资源服务器可以通过验证签名来验证自包含访问令牌,无需与授权服务器通信。
下面是 OAuth 2.0 端到端认证流程的步骤:
1. 用户通过 Web 应用程序/移动应用程序信任的身份提供者(通过 OpenID Connect,也可以是 SAML 2.0)登录到 Web 应用程序/移动应用程序。OpenID Connect 是基于 OAuth 2.0 构建的身份联合协议,SAML 2.0 是另一种类似的身份联合协议。
2. Web 应用程序获得 OAuth 2.0
access_token
、
refresh_token
和
id_token
。
id_token
用于向 Web 应用程序识别最终用户。如果使用 SAML 2.0,Web 应用程序需要与信任的 OAuth 授权服务器的令牌端点通信,并根据 OAuth 2.0 的 SAML 2.0 授权类型将 SAML 令牌交换为 OAuth
access_token
。每个
access_token
都有过期时间,当
access_token
过期或接近过期时,OAuth 客户端可以使用
refresh_token
与授权服务器通信(无需最终用户参与)以获取新的
access_token
。
3. Web 应用程序代表最终用户调用 API,并在 API 请求中传递
access_token
。
4. API 网关拦截来自 Web 应用程序的请求,提取
access_token
,并与令牌交换端点(或 STS)通信,该端点将验证
access_token
,然后向 API 网关颁发签名的 JWT。这个 JWT 也会携带用户上下文。在 STS 验证
access_token
时,它将通过 API(如 RFC 7662 中定义的 Introspection API)与相应的 OAuth 授权服务器通信。
5. API 网关将 JWT 与请求一起传递给下游微服务。
6. 每个微服务将验证收到的 JWT,然后在进行下游服务调用时,可以创建一个由自身签名的新 JWT 并随请求一起发送。另一种方法是使用嵌套 JWT,新 JWT 也会携带前一个 JWT。还有一种方法是每个微服务与安全令牌服务通信,将收到的令牌交换为新令牌,以便与其他下游微服务通信。
下面是 OAuth 2.0 端到端认证流程的 mermaid 流程图:
graph LR
A[用户登录(OpenID Connect/SAML 2.0)] --> B[Web 应用获取令牌]
B --> C[Web 应用调用 API 传递 access_token]
C --> D[API 网关拦截请求]
D --> E[API 网关与 STS 通信获取 JWT]
E --> F[API 网关传递 JWT 到下游微服务]
F --> G[微服务验证 JWT 并处理]
通过这种方法,只有来自外部客户端的 API 调用会通过 API 网关。当一个微服务与另一个微服务通信时,不需要通过网关。从给定微服务的角度来看,无论请求来自外部客户端还是其他微服务,收到的都是 JWT,因此这是一种对称的安全模型。
微服务安全基础:JWT、TLS 与 OAuth 2.0 详解
8. 总结与应用建议
在微服务架构中,安全是至关重要的。JWT、TLS 相互认证和 OAuth 2.0 等技术为微服务安全提供了强大的支持,但在实际应用中需要根据具体场景进行合理选择和配置。
对于 JWT 的使用,需要注意以下几点:
- 确保 JWT 声明集中的声明名称唯一,避免解析错误。
- 根据应用需求定义必需和可选声明,如 OpenID Connect 规范中的
iss
、
sub
等。
- 注意 JWT 的签名算法和密钥管理,确保令牌的安全性。
- 合理设置
aud
参数,明确令牌的受众,避免令牌被滥用。
在 TLS 相互认证方面:
- 每个微服务都需要拥有自己的证书,确保通信双方的身份验证。
- 对于证书撤销,根据实际情况选择合适的方法,如 OCSP Stapling 可以减少客户端与 OCSP 响应器的通信开销。
- 考虑使用短期证书,减少证书管理的复杂性,但要确保底层平台支持动态更新。
OAuth 2.0 的应用建议如下:
- 根据不同的应用场景选择合适的授权类型,如授权码授权类型适用于代表用户访问资源的场景,客户端凭证授权类型适用于系统以自身身份访问 API 的场景。
- 对于 OAuth 客户端,使用更强大的凭证,如证书或 JWT,提高安全性。
- 了解不同类型的访问令牌(引用令牌和自包含令牌)的特点,根据需要进行选择。
9. 安全模型对比
为了更清晰地了解不同安全方法的特点,下面对 JWT、TLS 相互认证和 OAuth 2.0 进行对比:
| 安全方法 | 身份携带 | 证书管理 | 令牌验证 | 适用场景 |
| ---- | ---- | ---- | ---- | ---- |
| JWT | 可携带最终用户和上游服务身份 | 依赖密钥对 | 验证签名 | 微服务间传递用户上下文 |
| TLS 相互认证 | 最终用户身份需在应用层传递 | 每个微服务需有证书,证书撤销较复杂 | 验证证书签名 | 微服务间相互身份验证 |
| OAuth 2.0 | 依赖令牌携带信息 | 授权服务器管理 | 验证令牌有效性 | 外部客户端访问微服务 |
10. 未来趋势与挑战
随着微服务架构的不断发展,安全领域也面临着新的挑战和趋势。
挑战
:
- 信任引导问题仍然是一个难题,需要更有效的解决方案来建立微服务之间的信任。
- 证书管理在大规模微服务部署中变得更加复杂,需要更高效的证书颁发和撤销机制。
- 随着技术的发展,新的安全漏洞可能会不断出现,需要及时更新安全策略和技术。
趋势
:
- 更多地使用自动化工具来管理微服务安全,如自动证书颁发和更新。
- 结合人工智能和机器学习技术来检测和预防安全威胁。
- 标准化的安全框架和协议将得到更广泛的应用,提高微服务安全的互操作性。
11. 实践案例分析
为了更好地理解这些安全技术的应用,下面通过一个实际案例进行分析。
假设一个电商平台采用微服务架构,包含用户服务、商品服务、订单服务等多个微服务。外部用户通过 Web 应用程序访问平台,这就涉及到安全认证和授权的问题。
- 身份验证 :用户通过 OpenID Connect 登录到 Web 应用程序,Web 应用程序获得 OAuth 2.0 令牌。例如,用户使用 Google 账户登录,Google 作为身份提供者,Web 应用程序通过 OpenID Connect 协议获取用户信息和令牌。
- API 访问 :Web 应用程序在调用微服务 API 时,将 OAuth 2.0 令牌传递给 API 网关。API 网关验证令牌的有效性,并将用户上下文封装在 JWT 中传递给下游微服务。例如,用户在 Web 应用程序中查看商品列表,Web 应用程序调用商品服务的 API,API 网关处理令牌并传递 JWT 给商品服务。
- 微服务间通信 :微服务之间使用 TLS 相互认证来确保通信的安全性。例如,订单服务在调用用户服务获取用户信息时,通过 TLS 握手验证双方的身份。
通过这个案例可以看出,综合使用 JWT、TLS 相互认证和 OAuth 2.0 可以有效地保障微服务架构的安全性。
12. 总结
微服务安全是一个复杂而重要的领域,涉及到多个方面的技术和策略。JWT、TLS 相互认证和 OAuth 2.0 为微服务安全提供了强大的支持,但在实际应用中需要根据具体情况进行合理选择和配置。通过了解这些技术的原理和应用场景,以及应对未来的挑战和趋势,可以更好地保障微服务架构的安全性和可靠性。
在实际开发中,建议开发者深入学习这些安全技术,结合实际业务需求进行实践,不断优化和完善微服务安全体系。同时,关注行业的最新动态和研究成果,及时应用新的安全技术和方法,以应对不断变化的安全威胁。
下面是一个总结微服务安全技术应用流程的 mermaid 流程图:
graph LR
A[外部用户登录(OpenID Connect/OAuth 2.0)] --> B[Web 应用获取令牌]
B --> C[Web 应用调用 API 传递令牌]
C --> D[API 网关验证令牌并生成 JWT]
D --> E[API 网关传递 JWT 到下游微服务]
E --> F[微服务间 TLS 相互认证]
F --> G[微服务处理请求]
通过以上的分析和总结,希望能够帮助读者更好地理解微服务安全的基础知识和应用方法,为构建安全可靠的微服务架构提供参考。
超级会员免费看
188

被折叠的 条评论
为什么被折叠?



