题干与适用场景
设计一个面向多租户平台的 Secrets 管理服务。Secret 可以是数据库凭据、API Token、私钥或证书。 系统保存 2000 万个活跃 Secret 名称,平均每个保留 3 个版本;值平均为 2 KiB,最大不超过 64 KiB。 峰值流量为每秒 10 万次读取和 2000 次版本写入。Secret 所属地域内的成功读取 p99 应低于 50 毫秒。
每个地域跨 3 个可用区部署。所属地域已经确认的写入必须承受 1 个可用区故障。整个地域失效后的灾备 目标是 RTO 15 分钟、RPO 1 分钟。这些规模和目标都是面试假设,不是任何商业 Secrets 产品公开的 性能上限。如果业务要求地域级故障后也不丢失任何已确认写入,候选人必须明确采用同步跨地域共识或 等价的持久性边界,并承担相应的延迟与可用性代价。
威胁模型包括数据库快照被窃、备份泄露、跨租户授权错误、普通存储运维人员越权、明文误入日志,以及 单个数据面主机被攻破。系统不声称能阻止一个已经获准取值的工作负载使用该值;它要缩小工作负载权限、 缩短凭据寿命、保留访问归因,并限制单点失陷的爆炸半径。
范围包括工作负载认证、策略求值、静态与动态 Secret、加密、不可变版本、分阶段轮换、撤销、租约、 审计、复制和恢复。组织身份提供方、真正接受动态凭据的数据库,以及通用密码学密钥管理服务属于外部 依赖。2026 年仍可访问的安全工程面试资料包含“设计 Secrets 管理服务”这一具体系统设计题,也把微服务 Secrets 管理系统列为安全系统设计练习。这能说明题目的当前代表性,不能证明任何特定公司的出题频率。
面试官考察点
第一项信号是能否定义准确的保护边界。只说“数据库加密”可能让解密密钥与密文放在同一边界,也没有 说明哪类失陷会被隔离。优秀答案会拆开由 HSM 或 KMS 保护的根、租户级密钥加密密钥、每版本数据加密 密钥和普通密文存储,并指出明文会在哪些位置短暂出现。
第二项信号是每次请求都经过授权。租户隔离不能依赖调用方提供的路径前缀。服务应从已认证身份推导 租户、工作负载、环境和角色,对准确资源与动作执行版本化策略,并把这些事实写入审计事件。认证、授权、 加密与审计是四类不同控制。
第三项信号是生命周期推理。创建新 Secret 值、更换密码学封装密钥、修改外部数据库接受的凭据是三种 不同操作。跨外部系统的轮换不是一笔原子数据库事务,需要持久阶段、重叠规则、幂等、验证、补偿与对账。
第四项信号是如实说明撤销语义。拒绝后续读取不能擦掉进程已经复制的静态密码。有效的紧急撤销还要在 真正接受凭据的系统中禁用或轮换它。动态凭据可以提供更强的到期与撤销语义,因为目标系统参与租约。
最后要平衡延迟、安全和可用性。让 10 万次每秒的读取逐次访问 HSM,通常会把信任根变成错误的瓶颈; 把明文放进共享分布式缓存也不是正确补救。答案应使用密钥层级、范围受控的保护内存缓存、明确的策略 陈旧上限和经过验证的故障语义。
回答前需要澄清的问题
- 支持哪些 Secret 类型? 不透明静态值需要版本存储;数据库用户和证书还可能支持动态签发与目标端撤销。
- 谁可以读取 Secret? 优先使用工作负载身份和短时会话。若允许人直接查看,必须增加审批、二次认证和独立审计策略。
- 撤销承诺是什么? 服务可以立即拒绝新读取;让已经复制的静态值失效,需要下游系统轮换或禁用该凭据。
current需要怎样的一致性? 本方案让所属地域内每个 Secret 的发布与撤销线性一致;调用方也可明确请求不可变版本。- 地域故障是否要求零数据丢失? 题设 DR RPO 为 1 分钟。RPO 为零需要同步跨地域确认,会改变延迟和可用性取舍。
- 应用能否在服务故障时使用本地缓存? 只有明确策略才可允许部分静态 Secret 使用加密、限时的 Agent 缓存,并承认即时撤销会变弱。
- 值与版本有多大? 题设限制单值 64 KiB,平均保留 3 个版本。大文档应进入另一套加密对象存储设计。
- 审计必须记录什么? 主体、工作负载、租户、资源标识、动作、策略版本、结果、请求 ID、地域和时间,绝不记录明文值。
- 谁能管理信任根? 需要职责分离、多人批准的紧急访问和恢复演练。日常使用的万能管理员会破坏租户隔离。
30 秒回答框架
“我会把策略与写入控制面和低延迟读取数据面分开。工作负载先用短时平台身份认证;服务从身份推导租户, 再对准确 Secret 与动作执行版本化最小权限策略。每个不可变版本使用随机数据密钥和认证加密,数据密钥由 租户密钥封装,HSM 或 KMS 保护解封租户密钥的根;普通存储只能看到密文与被封装密钥。地域事务共识通过 比较更新,原子追加版本并移动 current 指针。轮换采用创建、安装、验证、发布、重叠和退役的持久工作流, 因为外部目标无法加入同一事务。动态凭据带有可续期或撤销的租约。我会缓存密文,只在加固服务内存中短时 缓存解封密钥,绝不使用共享明文缓存。审计进入独立保护的追加式管道。验证覆盖跨租户拒绝、并发发布、 密钥丢失、KMS 故障、撤销卡住、日志泄露和完整地域恢复。”
分步骤深入解答
第一步:把题目转成不变量和威胁边界
先确定五条不变量:
1. A request can address only a tenant derived from authenticated identity.
2. Ordinary databases, queues, logs, traces, and backups never receive plaintext values or plaintext keys.
3. A published version is immutable; promotion only changes a version pointer.
4. Every allow and deny decision emits an audit event without the value.
5. Acknowledged regional writes survive one availability-zone failure.这些不变量仍然留下残余风险:获准工作负载会拿到明文;数据面进程会短暂持有解封密钥与明文;恶意信任根 管理员可能超越普通策略。可用短时身份、窄策略、进程隔离、禁用 core dump、保护内存、独立审计、职责 分离和更小密钥作用域降低风险。不能声称静态加密解决了运行时失陷。
第二步:分开控制面和读取数据面
控制面管理租户、策略、Secret 元数据、新版本、轮换任务、审批和紧急动作。它的写入量较低,更强调严格 校验和可审计工作流。数据面负责认证工作负载、求值策略、解密获准版本并通过双向认证 TLS 返回,热路径 必须保持精简。
每个租户有一个所属地域,权威 Secret 与策略写入都在此完成。地域内跨 3 个可用区的事务共识提交元数据、 密文、版本指针、租约、幂等记录与审计 outbox。读取节点可以横向扩展,但 current 读取要访问强一致存储, 或使用已经证明读屏障的副本;陈旧副本不得复活已撤销版本。
身份来自平台身份提供方,例如工作负载证书或签名服务账号 Token。Secrets 服务校验签发方、受众、到期时间、 工作负载标识和通道绑定,再把可信身份映射为租户与角色。客户端不能通过请求头覆盖这个租户。
第三步:定义精简 API 与不可变数据模型
一组可用的核心 API 是:
POST /v1/secrets/{path}/versions
{ value, idempotencyKey, expectedCurrentVersion? }
-> { secretId, version, status: "PENDING" }
POST /v1/secrets/{path}/versions/{version}/promote
{ expectedCurrentVersion, idempotencyKey }
-> { currentVersion }
GET /v1/secrets/{path}?version=current
-> { value, version, expiresAt? }
POST /v1/dynamic/{role}/credentials
{ requestedTtl, idempotencyKey }
-> { value, leaseId, expiresAt, renewable }
POST /v1/leases/{leaseId}/renew
POST /v1/leases/{leaseId}/revoke路径是资源名,不是授权证据。服务端只做一次规范化,拒绝歧义编码,从身份推导租户,并检查动作级策略。
Secret(secret_id, tenant_id, canonical_path, state,
current_version, policy_ref, created_at)
SecretVersion(secret_id, version, status, ciphertext, nonce, auth_tag,
wrapped_dek, tenant_kek_version, content_fingerprint_hmac,
created_by, created_at)
Policy(policy_id, tenant_id, version, document, state, created_at)
Lease(lease_id, tenant_id, secret_id, principal_id, target_ref,
status, expires_at, renewable_until, last_error)
Idempotency(tenant_id, principal_id, operation, key,
request_fingerprint_hmac, result_ref, expires_at)(tenantid, canonicalpath) 与 (secret_id, version) 分别唯一。版本行的加密载荷永不修改。发布通过 current_version 条件更新完成;两个管理员都从版本 7 发起发布时,不能互相静默覆盖。审计 outbox 与每次 状态变更同事务提交,再导出到独立控制的存储。指纹必须使用独立租户密钥计算带密钥 HMAC,不能直接保存 低熵凭据的普通哈希,否则被窃快照可以离线猜测原值。
第四步:建立信封加密密钥层级
每个版本生成随机数据加密密钥,使用认证加密算法加密 Secret 值。把 tenantid、secretid、版本与算法 标识作为附加认证数据绑定,密文被移动到另一个租户或版本时就会校验失败。密文、nonce、认证 Tag 和被封装 的数据密钥一起保存。
每个租户拥有一个或多个版本化密钥加密密钥。由 HSM 或云 KMS 保护的根,只为获准数据面节点解封这些租户 密钥;租户密钥再封装每个版本的数据密钥。因此普通存储快照缺少解密材料。单个数据面主机被窃会暴露该 进程当时持有的租户密钥和数据密钥,节点放置与缓存作用域必须限制它能服务多少租户。
HSM/KMS root
-> unwrap tenant KEK version
-> unwrap per-secret-version DEK
-> AEAD-decrypt secret value with bound tenant/version metadata每次读取都调用 HSM 会让根服务成为延迟和吞吐瓶颈。数据面节点可以按租户风险分区,在加固内存中短时缓存 解封后的租户密钥或数据密钥;策略或密钥事件到达以及进程退出时清空。共享 Redis、磁盘 swap、崩溃转储、 日志与 Trace 都不能接收明文或解封密钥。
更换租户封装密钥时,可以重新封装数据密钥而无须解密每个 Secret 值。修改数据库密码则会创建新的 Secret 值,并与数据库协调。两者爆炸半径不同,不能共用一个含糊的“轮换”按钮。
第五步:让授权与缓存理解撤销
策略定义 create-version、promote、read、issue-dynamic、renew、revoke 和 administer-policy 等动作, 可限制租户、项目、环境、路径、工作负载、网络区、时间和最大 TTL;显式拒绝优先。默认禁止人直接读取 明文。获批例外要记录工单、审批人、理由和短时授权。
策略在本地求值以满足延迟要求,但缓存必须带版本和最大寿命。撤销策略或主体时,先提交变更,再发布失效 事件并推进租户授权 epoch。读取节点必须证明自己的 epoch 不低于要求。如果失效通道不可用且缓存超过最大 寿命,敏感读取应 fail closed,不能永久信任旧策略。
密文和不可变元数据可以广泛缓存,明文不可以。工作负载侧 Agent 可以在声明 TTL 内,把获准值保存在自身 内存中,减少重复读取。如果产品允许某些可用性关键的静态 Secret 使用加密磁盘缓存,就必须明确定义设备 绑定密钥、到期时间和较弱的撤销承诺。控制面故障时盲目返回旧值,不应成为通用回退。
第六步:把凭据轮换做成可恢复工作流
跨目标数据库的静态凭据轮换遵循以下持久阶段:
CREATED_PENDING
-> INSTALLED_AT_TARGET
-> VERIFIED
-> PROMOTED_CURRENT
-> OLD_VERSION_IN_OVERLAP
-> OLD_VERSION_REVOKED
-> COMPLETE每个转移都有幂等 Connector 操作和已记录证据。先创建新凭据,在目标端安装,通过预期路径验证,再发布 新版本;若消费者需要则保留有限重叠,最终在目标端禁用旧凭据。对账 Worker 比较工作流和目标端状态, 在崩溃后续跑。若安装成功但响应丢失,查询必须发现既有结果,不能再创建一个凭据。
若旧值疑似泄露,紧急轮换可以跳过重叠,这会造成应用中断,需要事故范围内的专用授权。仅禁止读取存储中 的旧版本还不够,因为目标系统可能仍接受它。
动态 Secret 由 Connector 为工作负载创建唯一短时凭据并登记租约。续期要返回实际新到期时间,它可能 短于请求值。到期或人工撤销时,目标端必须让凭据失效。若目标端不可达,租约进入 REVOCATION_PENDING; 持续重试、告警和人工 Runbook 都要保持有效。系统不能因为消息已经入队就把它标记为撤销成功。
第七步:计算容量并隔离热点租户
2000 万个名称、每个保留 3 个版本、每个值 2 KiB,原始加密值约为:
20,000,000 × 3 × 2 KiB = 122,880,000,000 bytes ≈ 114 GiB如果每版本加密元数据平均再占 1 KiB,会增加约 57 GiB。地域内 3 副本的起始估算约为 513 GiB,还未计算 索引、审计、租约、幂等、备份和增长。这是题设推导出的容量基线,不是产品性能声明。
每秒 10 万次读取、平均返回 2 KiB,单是明文出口就接近 195 MiB/s,还没有计入 TLS 与响应开销。按稳定 租户标识分片,再把异常热点或受监管租户放入独立 Cell。设置租户级并发和速率预算,避免一个被攻破的工作 负载耗尽所有解密 Worker 或审计容量。
读取延迟要拆成身份校验、策略求值、元数据查询、密钥解封或缓存、解密、审计入队和网络时间。HSM Cache Miss 与审计背压应有独立预算。不能为了保护 p99 静默丢弃审计;要么用持久本地或 outbox 边界,要么在 无法保留必需审计记录时拒绝敏感请求。
第八步:分别设计地域恢复与信任根恢复
把加密状态与审计 outbox 异步复制到温备地域,并度量复制延迟。提升温备地域时使用 fencing epoch,避免 旧所属地域恢复后成为第二个主写入方。按照题设 1 分钟 RPO,整个地域失效后,最新的部分已确认版本可能 需要重建;如果不可接受,就必须在确认前完成跨地域事务共识。
灾备地域还需要独立可用的信任根、工作负载身份、策略状态和审计出口。只复制密文不算恢复方案。要验证 加密快照和时间点恢复,并单独保护配置、启动凭据和自动解封材料,因为即使数据快照已加密,这些配置仍 可能敏感。
信任根恢复需要多人控制的紧急流程、职责分离、不可篡改证据和定期演练。丢失租户密钥可能永久失去该租户 值;泄露租户密钥可能暴露它封装的全部版本,因此密钥备份、轮换、放置和销毁都必须有生命周期测试。
第九步:验证安全属性与故障语义
测试应围绕不变量,而不只看正常延迟:
- 生成跨租户路径、编码路径、通配策略和陈旧身份请求,证明全部被拒绝。
- 并发执行版本创建、发布、策略撤销和读取,确认陈旧节点不会返回刚被禁止的版本。
- 扫描数据库、队列、日志、Trace、崩溃转储和备份中的诱饵明文与解封密钥材料。
- 注入 KMS 延迟、密钥缓存清空、存储共识丢失、审计背压和完整控制面故障。
- 在目标端动作前后中断每个轮换阶段,证明对账能收敛且不会重复创建凭据或虚报完成。
- 让动态 Secret 的目标端在到期时不可用,证明租约持续显示待撤销,直到目标端失效完成。
- 从加密复制与快照恢复灾备地域,验证 fencing、身份、策略、密钥、审计、RTO 和实测 RPO。
- 以高度倾斜的租户分布压测每秒 10 万次读和 2000 次写,同时度量 p99、拒绝准确率、HSM 流量、
缓存作用域和审计完整性。
高质量示范回答
“我先明确系统防护存储、备份、跨租户、日志泄露和有限的主机失陷,但获准工作负载仍可能滥用它拿到的值。 每个请求从短时工作负载身份开始。服务从身份推导租户和工作负载,对准确资源与动作执行版本化策略;调用方 提供的路径不能选中另一个租户。
数据采用不可变版本。创建请求写入待发布版本,发布通过比较预期旧版本,条件移动 Secret 的 current 指针。所属地域跨 3 个可用区的共识,把密文、元数据、幂等记录和审计 outbox 一起提交。current 读取使用 读屏障,陈旧副本不能返回已撤销版本。
加密方面,每个版本生成随机数据密钥,用绑定租户、Secret 与版本的认证加密处理。租户密钥封装数据密钥, HSM 或 KMS 保护的根只在获准数据面节点解封租户密钥。普通存储与备份只含密文和被封装密钥。读取节点可以 在按租户隔离的加固内存中短时缓存解封密钥;明文永不进入共享缓存、日志、Trace、swap 或 core dump。
凭据轮换是一条状态机:创建、在目标端安装、验证、发布、重叠、撤销旧目标凭据、完成。每个 Connector 调用都幂等并参与对账,因为外部数据库无法加入元数据事务;疑似泄露时可取消重叠。动态凭据按工作负载 签发并带租约;只有目标端确认撤销才算到期完成,否则系统保持可见待处理状态并告警。
控制面负责策略、版本、审批和工作流;横向扩展的数据面 Cell 服务读取。密文可以广泛缓存,策略缓存有版本 与寿命,解封密钥只短时保存在本地。地域失效时,加密状态复制到 RPO 1 分钟的温备地域,fencing epoch 阻止脑裂。如果需求变为 RPO 为零,我会同步跨地域提交,并接受更高延迟或更低写可用性。
最后验证跨租户和编码路径攻击、并发发布与撤销、全部观测和备份表面的诱饵泄露、KMS 与审计故障、轮换 每一步崩溃、租约撤销卡住,以及完整灾备恢复。只有故障下注入后的安全判断仍正确,并且每秒 10 万次读取 仍满足分解后的延迟预算,方案才算通过。”
常见错误
- 把一个数据库加密密钥放在数据库旁边 → 快照失陷可能同时获得密文和解密路径 → 拆开 HSM/KMS 根、租户封装密钥、每版本数据密钥和普通存储。
- 信任请求里的租户或路径 → 修改对象名就可能跨租户访问 → 从可信身份推导租户,只规范化一次并授权准确动作和资源。
- 为排障记录请求或响应正文 → 观测系统会成为明文 Secret 仓库 → 只记录标识、决策与版本,并用诱饵值测试泄露。
- 每次读取都访问 HSM → 信任根延迟与配额成为平台瓶颈 → 使用信封加密和明确爆炸半径的限时保护内存缓存。
- 把解密值放入 Redis → 延迟优化扩大了明文失陷边界 → 广泛缓存密文,明文只进入获准消费者的限时内存。
- 把轮换当成一次更新 → 目标端安装与元数据提交可能一边成功一边失败 → 采用持久分阶段工作流、幂等 Connector、重叠规则和对账。
- 消息入队就把租约标为已撤销 → 下游凭据可能仍有效 → 在目标端确认失效前保持待处理,并对卡住撤销告警。
- 承诺立即撤回已复制的静态值 → 新读取能被拒绝,既有副本仍可用 → 在接受系统轮换或禁用,并优先使用短时动态凭据。
- 无限期使用陈旧策略并 fail open → 已移除主体可以继续取值 → 给策略缓存加版本、失效和最大寿命,超限后 fail closed。
- 复制密文到另一个地域就宣称完成 DR → 身份、根密钥、策略、fencing 和审计仍可能不可用 → 验证完整依赖图并度量真实 RTO 与 RPO。
面试追问
追问一:Secrets 管理服务和 KMS 有什么区别?
Secrets 管理服务存储并版本化不透明凭据,授权取值,协调轮换,签发租约并审计访问。KMS 管理密码学密钥, 提供封装或签名等操作。本方案把 KMS 或 HSM 当成信任根,不把根密钥作为普通可读取 Secret 暴露。
追问二:应用可以缓存 Secret 吗?
可以,但必须有明确契约。Agent 可在有界 TTL 内把获准值留在进程内存,并在到期前刷新。这会降低读取量和 可用性依赖,也会延长仅靠策略撤销无法消除既有副本的窗口。高风险或动态凭据应使用更短租约和目标端撤销, 不能无限缓存。
追问三:KMS 或 HSM 不可用时怎么办?
节点只能在获批缓存寿命内继续使用已经解封的密钥。新租户、Cache Miss 与根密钥操作 fail closed。监控 缓存覆盖、KMS 错误率与剩余安全窗口,在所有节点同时到期前主动限流。事故中延长密钥寿命是需要预先规则的 安全决定,不能由重试循环自动完成。
追问四:怎样在不解密每个 Secret 的情况下轮换租户密钥?
用旧租户密钥解封每版本数据密钥,再用新租户密钥封装;最好在可信密钥服务内部完成,使明文数据密钥不离开 边界。记录租户密钥版本和被封装数据密钥,给迁移设置检查点,在所有引用与备份策略允许前保留旧租户密钥。 Secret 密文本身不需要改变。
追问五:如何阻止高权限运维读取所有租户 Secret?
分开策略管理、密钥管理、基础设施运维和审计复核。默认关闭人类明文读取;例外采用多人批准的短时授权; 受监管租户使用独立 Cell 与密钥作用域;证据进入独立审计系统。任何日常角色都不应既能给自己授权,又能 无外部证据地解密值。
追问六:所属地域宕机后,读取是否应该继续?
只有灾备地域拥有已 fencing 的权威 epoch、足够新的策略和加密数据、可用信任根,并且业务接受明确 RPO 时 才继续。任意陈旧副本可能复活撤销权限。题设 RPO 1 分钟时,按演练流程提升温备地域;RPO 为零需要事故前 就启用同步跨地域提交。
追问七:为什么不只用环境变量保存 Secret?
环境变量是交付方式,不是生命周期管理。它可能通过进程检查、崩溃转储、诊断、子进程或部署配置泄露,也 缺少集中版本、目标端轮换、租约和逐次读取归因。若部署 Agent 注入环境变量,也要缩小范围与寿命,并继续 让 Secrets 管理服务承担策略与生命周期事实源。