跳到主要内容

微服务中的身份验证和授权

需求

  • 设计一个从简单开始但可以随着业务扩展的身份验证解决方案
  • 考虑安全性和用户体验
  • 讨论该领域的未来趋势

大局观:身份验证(AuthN)、授权(AuthZ)和身份管理

首先,回归基础

  • 身份验证:确定你是谁
  • 授权:确定你可以做什么

在开始时……让我们有一个简单的服务……

  • 分层架构
  • 客户端存储一个cookie或token作为登录状态的证明。(代客钥匙模式)

  • 服务器持久化相应的会话
  • token通常采用JWT格式,由从安全位置(环境变量、AWS KMS、HashiCorp Vault等)获取的密钥签名

  • 流行的Web框架通常提供开箱即用的身份验证解决方案

然后,随着业务的增长,我们用AKF规模立方体来扩展系统:

  • X轴:水平克隆
  • Y轴:功能分解
  • Z轴:分片

加上康威定律:组织设计的系统反映其沟通结构。我们通常将架构演变为微服务(参见为什么选择微服务?了解更多)

  • 顺便说一句,"微服务与单体"和"多仓库与单仓库"是不同的概念。
  • 对于企业,有员工身份验证和客户身份验证。我们更关注客户身份验证。

在微服务世界中,让我们抽取身份验证和授权服务的功能切片,并有一个身份和访问管理(IAM)团队在进行相关工作。

  • 身份感知代理 是一个反向代理,允许公共端点或检查受保护端点的凭证。如果凭证未提供但被要求,则将用户重定向到身份提供者。例如:k8s ingress controller、nginx、envoy、Pomeriumory.sh/oathkeeper等。
  • 身份提供者和管理者 通过登录、忘记密码、MFA注册等工作流管理用户身份。例如:ory.sh/kratoskeycloak
  • OAuth2和OpenID Connect提供者 为第一方登录(OIDC)签发令牌,并允许第三方开发者通过OAuth2接入。实践中通常与身份提供者合并在同一服务中——Keycloak、Auth0和Okta均同时具备两者——但也可分离:例如使用Ory Kratos管理身份 + Ory Hydra处理OAuth2/OIDC。
  • 授权 服务控制谁可以做什么。

身份验证

1. 身份提供者

  • 最简单的解决方案是提交用户的身份证明并发放服务凭证。
    • 密码哈希推荐使用 Argon2id(OWASP当前首选)、bcrypt 或 scrypt
  • 然而,现代应用程序通常处理复杂的工作流,如条件注册、多步骤登录、忘记密码等。 这些工作流本质上是状态机中的状态转换图。

工作流:用户设置和个人资料更新

Ory.sh/Kratos作为示例架构

2. 第三方OAuth2

OAuth2让用户或客户端经历几种授权类型(不确定使用哪个?请查看这个),如

  1. 授权码 + PKCE 授权 — Web(SPA)和移动/原生应用
  2. 客户端凭证授权 — 后端/机器间(M2M)服务
  3. 设备授权授权 — 输入受限的设备(智能电视、CLI工具、物联网)

然后最终获得访问令牌和刷新令牌

  1. 访问令牌是短期有效的,因此如果被泄露,攻击窗口很短
  2. 刷新令牌一次性使用并在每次使用后轮换;机密客户端(服务端)将其与客户端密钥配对,而公共客户端(SPA、移动端)依赖轮换刷新令牌——存储在 httpOnly cookie 中,切勿存入 localStorage

假设在这个工作流中涉及许多实体——客户端、资源拥有者、授权服务器、资源服务器、网络等。更多的实体会增加被攻击的暴露面。一个全面的协议应该考虑所有边缘情况。例如,如果网络不是 HTTPS / 不能完全信任怎么办?

OpenID Connect 是构建在 OAuth2 之上的身份协议,它添加了携带用户身份声明的签名 ID 令牌(JWT),从而实现跨服务的单点登录(SSO)。

在这些工作流和令牌处理过程中有很多棘手的细节。不要重新发明轮子。

3. 服务间身份验证

在微服务中,服务之间同样需要互相验证身份——这与面向用户的身份验证不同,但经常被忽视:

  • mTLS(双向TLS) — 双方均出示证书;服务网格(Istio、Linkerd)通过 sidecar 透明地处理此过程。身份绑定到工作负载,而非人类用户。
  • SPIFFE/SPIRE — 工作负载身份标准。每个服务获得一个加密的 SVID(SPIFFE 可验证身份文档),可与 mTLS 或 JWT-SVID 配合使用。
  • JWT传播 — 将原始用户 JWT 转发到下游服务,或由网关为下游服务签发新的服务令牌,从而无需每个服务重新验证用户凭证即可完成授权。

不要在每个服务中各自实现令牌验证——使用共享库或 sidecar 统一处理。

4. 多因素身份验证

问题:凭证填充攻击

用户倾向于在多个网站上重复使用相同的用户名和密码。当其中一个网站遭遇数据泄露时,黑客会使用这些泄露的凭证对其他网站进行暴力攻击。

  • 多因素身份验证:短信、电子邮件、电话语音一次性密码、身份验证器 TOTP
  • 速率限制器、失败禁止和异常检测

挑战:电子邮件或短信的送达率差

  • 不要将营销电子邮件渠道与事务性渠道共享。
  • 语音一次性密码通常具有更好的送达率。

5. 无密码

  1. 通行密钥(WebAuthn/FIDO2) — 当前防钓鱼无密码身份验证的标准。所有主流平台(Apple、Google、Microsoft)和浏览器均原生支持。凭证是绑定到设备和源域名的私钥,从结构上杜绝了钓鱼攻击。推荐所有新实现优先采用。

    • 同步通行密钥(iCloud 钥匙串、Google 密码管理器)支持跨设备使用
    • 硬件安全密钥(YubiKey)适用于高保障场景
  2. 生物识别:指纹、面部识别——通常通过底层使用 WebAuthn 的平台身份验证器实现。权衡依然存在

  3. 推送通知——被 Duo、Okta Verify 等应用采用

客户端如何订阅服务器的状态?短轮询、长轮询、WebSocket 或服务器推送事件。

6. 市场上的供应商

不要重新发明轮子。

7. 优化

挑战1:Web登录非常慢或根本无法提交登录表单。

  • JS包对于移动Web来说太大
    • 构建一个轻量级的PWA版本的SPA(单页面Web应用)。无论如何使包小——例如,preact或inferno
    • 或者根本不使用SPA。简单的MPA(多页面Web应用)与原始HTML表单提交效果很好
  • 浏览器兼容性
    • 使用BrowserStack或其他工具在不同浏览器上进行测试
  • 数据中心距离太远
    • 将静态资源放到边缘/CDN,并通过Google骨干网中继API请求
    • 建立一个本地数据中心 😄

请参见Web应用交付优化以获取更多信息

挑战2:账户接管

挑战3:账户创建耗时过长

当后端系统变得过于庞大时,用户创建可能会分散到许多服务,并在不同数据源中创建许多条目。在注册结束时等待15秒感觉很糟糕,对吧?

  1. 收集并逐步注册
  2. 异步

授权

isAuthorized(subject, action, resource)

1. 基于角色的访问控制(RBAC)

2. 基于策略的访问控制(PBAC / ABAC)

{
"subjects": ["alice"],
"resources": ["blog_posts:my-first-blog-post"],
"actions": ["delete"],
"effect": "allow"
}

挑战:单点故障和级联故障

  • 预处理和缓存权限
  • 利用请求上下文
    • 假设:数据中心内部的请求是可信的,而外部请求则不可信
  • 失败开放与失败关闭

3. 基于关系的访问控制(ReBAC)

Google Zanzibar 论文开创,该模型将权限表示为用户与对象之间关系的图谱——例如,"alice 是 document:X 的编辑者,该文档位于 folder:Y 中,而 alice 的团队对该文件夹有查看权限。"权限通过图谱递归组合,支持针对数十亿对象的细粒度权限检查。

  • 开源实现:OpenFGA(由Okta出品)、SpiceDBOso
  • 适用场景:文档共享、多租户 SaaS、社交图谱——任何权限源自数据关系的场景

隐私

1. 个人可识别信息(PII)、受保护的健康信息(PHI)、支付卡行业(PCI)

西方文化有尊重隐私的传统,尤其是在纳粹杀害数百万人之后。 以下是一些典型的敏感数据类型:个人可识别信息(PII)、受保护的健康信息(PHI,受HIPAA监管)和信用卡或支付卡行业(PCI)信息。

2. 差分隐私

仅仅删除敏感信息可能不足以防止与其他数据集相关的数据泄露。

差分隐私帮助分析师从包含个人信息的数据库中提取数据,但仍然保护个人隐私。

3. 去中心化身份

为了将身份与集中式身份提供者及其相关的敏感数据解耦,我们可以使用去中心化身份(DID)

  • 它本质上采用URN格式:did:example:123456789abcdefghijk
  • 它可以从非对称密钥及其目标业务领域派生。
    • 与传统方式不同,它不涉及你的个人信息
    • 请参见DID方法了解其如何与区块链一起工作。
  • 它通过以下方式保护隐私
    • 为不同目的使用不同的DID
    • 选择性披露/可验证声明

想象一下,艾丽斯有一个州政府颁发的DID,想在不透露真实姓名和确切年龄的情况下购买一些酒。

饮酒

一个DID解决方案:

  • 艾丽斯有一个身份档案,包含did:ebfeb1f712ebc6f1c276e12ec21、姓名、头像URL、生日和其他敏感数据。
  • 创建一个声明,表明did:ebfeb1f712ebc6f1c276e12ec21超过21岁
  • 一个受信任的第三方签署该声明,使其成为可验证声明
  • 使用可验证声明作为年龄的证明

总结

本文概述了微服务中的身份验证和授权,你不必记住所有内容才能成为专家。以下是一些要点:

  1. 遵循标准协议,不要重新发明轮子
  2. 不要低估安全研究人员/黑客的力量
  3. 完美是很难的,也不必完美。全面优先考虑你的开发工作
References:Want to keep learning more?