凤凰架构 · 第 5 章 架构安全性 —— 苏格拉底式学习笔记

来源:《凤凰架构:构建可靠的大型分布式系统》(周志明) 学习方式:苏格拉底式提问会话(本场覆盖 5.1 认证 / 5.2 授权(部分)/ 5.3 凭证 / 5.5 传输)


一、安全设计的六个基本问题

问题 英文 一句话
认证 Authentication 你是谁?(分辨用户真实身份)
授权 Authorization 你能干什么?(控制可见数据与可操作功能)
凭证 Credential 你如何证明?(系统与用户之间的承诺准确、完整、不可抵赖)
保密 Confidentiality 敏感数据如何不被窃取、滥用?
传输 Transport Security 网络传输的信息如何不被窃听、篡改、冒充?
验证 Verification 提交的数据是否合乎规则、不带来风险?

经验原则:安全上 99% 的系统都不该造轮子——以标准规范为指导、以标准接口去实现。


二、核心概念辨析

1. 认证 vs 授权 vs 凭证

  • 认证是一次性的"过程":登录那一刻,服务端核实你的身份。
  • 凭证是"认证结果"的物化载体:把"你曾通过认证"这个事实固化下来,让之后的每次请求免于重复认证
  • 授权:认证之后,决定你能碰什么。

关键洞察:服务端后续每次请求做的不是"认证",而是"验证凭证"——它默认持有有效凭证的人 = 当初通过认证的人。 例:攻击者偷走 Session ID 直接发请求,服务端照常放行——因为验证的是凭证,不是人。

2. Session vs JWT:状态放服务端还是客户端

维度 Cookie-Session JWT
状态存在哪 服务端(内存/存储) 客户端(令牌自带 payload)
验证方式 查表(sessionId → 用户状态) 用密钥重算签名验证有效性
信任源头 服务端存储 签名密钥保密
强制下线 删掉服务端 session 即可 做不到,只能加黑名单
水平扩展 需处理 session 共享(亲和/复制/集中三种,均受 CAP 限制) 天然无状态,任意加减节点
携带信息 服务端随意 受 Header 大小限制(Tomcat 8KB / Nginx 4KB)

关键洞察:"能否强制下线 / 能否主动失效 / 能否无状态扩展"全是"状态在哪"这一个区别推出来的。 JWT 是 Cookie-Session 在认证授权问题上的替代品,不是更先进,也不能全面取代——它只解决认证授权,且默认不加密(只防篡改、不防泄漏)。

3. 对称 vs 非对称签名(HMAC → RSA)

  • HMAC(HS256):签发与验证用同一个密钥(带密钥的哈希摘要)。
    • 后果:所有验证方都必须持有同一把密钥 → 密钥副本多、泄漏面大;一个最弱环节被攻破 = 全系统凭证体系崩塌(可伪造任意用户的合法 JWT)。
  • RSA(非对称)私钥签发(集中)、公钥验证(公开)
    • 后果:秘密只集中在签发服务;公钥泄漏了也造不出合法令牌。

关键洞察:微服务体系里签名从 HMAC 换 RSA,是因为信任支点从"所有服务共享一个秘密"变成了"秘密集中在签发方"。 副本越少,泄漏面越小。

4. 公钥分发的死循环与 PKI

  • 问题:公钥"公开"也得通过网络分发 → 中间人截获"拉公钥"的请求,返回自己的公钥 → 此后用它验证的一切签名全部失守。
  • 为什么不能"用私钥给公钥签名来证明":签名只能传递信任,不能凭空创造信任——验证签名本身又需要先信任另一个公钥 → 无限递归(蛋鸡悖论)。
  • 解法:锚点 = 出厂预置的根 CA 证书
    • 权威 CA 是"可数"的(全世界就那么些),可以不通过网络,提前装进浏览器/操作系统/服务节点的信任存储里。
    • 链条:预置的根证书(不需要证明)→ 验证 CA 签发的证书上的签名 → 取出服务真正的公钥
    • 信任的终点不是"证明",而是"物理预置 / 带外分发"。

任何人(包括你我)都可以签发证书,只是不权威罢了。

5. keystore vs truststore(每个服务节点上都有两个库)

存储 放什么 回答的问题 私钥
keystore 自己的证书(含自己的公钥)+ 自己的私钥 "我是谁" 绝对不出示、不传输
truststore 受信任的 根 CA 证书(含根公钥) "你是谁"(我信任谁)

类比:keystore = 我的身份证 + 签名章;truststore = 派出所档案室(我信任的机构名单)。

6. mTLS 的两条信任链(两个公钥、两个签名、两个时间点)

签名 ① 证书签名 签名 ② JWT 签名
谁签的 CA 用根私钥 account 服务用自己私钥
用什么验证 truststore 里根证书的公钥 从 B 证书里取出的 account 公钥
验证什么 B 的身份(证书是真的) 令牌内容(令牌可信)
何时发生 建立连接时(TLS 握手) 每次请求时(HTTP 层)

完整链条:

A 用 truststore 里根证书的公钥 验证 B 证书上的签名,确认身份,取出 account 的公钥;A 再用 account 的公钥 验证 JWT 上的签名,确认令牌可信。

两道关卡,缺一不可:

  • ① 证书必须由受信任的 CA 签发 → 挡住"假证书";
  • ② 通信方必须持有与证书公钥配对的私钥 → 挡住"偷了真证书但没私钥"的冒名者。
  • 前提:私钥永远不出 keystore

三、本场掉过的坑(常见误区清单)

  1. ❌ "认证 = 输入账号密码的过程" → ✅ 认证是一次性过程;后续验证的是凭证,免于重复认证。
  2. ❌ 把信任锚点说成"客户端证书" → ✅ 锚点是根 CA 证书;服务证书是被签发的中间产物,不是锚点。
  3. ❌ 根 CA 证书放 keystore → ✅ 验证别人的东西放 truststore;keystore 只放自己的证书 + 私钥。
  4. ❌ "重新签名去和原签名比对" → ✅ 验证 ≠ 比对:验证不需要知道原签名,只需用公钥重算并校验有效性。
  5. ❌ 证书签名和 JWT 签名混为一谈 → ✅ 两个签名、两个公钥、两个验证时机,分属"身份"与"内容"两条链。

四、一句话总结(复习用)

  • 凭证的本质:把"认证结果"物化,让后续请求免于重复认证;命门在服务端凭什么相信凭证有效。
  • Session vs JWT:状态在服务端(查表)vs 状态在客户端(验签)——一切差异由此而来。
  • 对称 → 非对称:把秘密从"人手一份"收敛到"集中一处",缩小泄漏面。
  • PKI 的答案:签名不能创造信任,锚点靠出厂预置的根 CA 证书一刀斩断死循环。
  • mTLS 两条链:证书验身份(连接时)、公钥验令牌(请求时);假证书和偷真证书无私钥都过不了关。