Skip to content
bitzorcas
中EN

Guide

Secret、签名密钥与许可证材料

管理 JWT、HMAC、Webhook、数据库、对象存储、OAuth、Data Protection 与运行时许可证的完整生命周期。

Last updated

Secret 管理不是把值从 appsettings.json 搬到环境变量。生产系统必须知道材料由谁拥有、工作负载如何取得、怎样轮换、旧版本何时撤销、泄露后如何恢复,以及哪些制品和租户受影响。

先按用途分类

类型当前例子推荐控制面
对称签名材料JWT fallback secret、HMAC、WebhookSecret Store/KMS,版本化轮换
非对称验证材料Runtime License trusted public keys配置可公开但需完整性与版本控制
连接凭据SQL、Redis、RabbitMQ、S3工作负载身份 + Secret Store
外部 ProviderOAuth、短信、邮件、AI API Key每 Provider 独立 Secret
应用可逆字段Provider key、线索敏感字段Data Protection 独立 purpose
校验型凭据API Key、SCIM token运行态 hash,不保留明文

公开密钥不需要保密,但仍不能被未授权替换。许可证验证的 trusted public key 若被攻击者改成自己的公钥,签名校验形式上仍会成功。

配置来源不是保管库

ASP.NET Core 可以从 JSON、user-secrets、环境变量、容器挂载文件或外部 Provider 读取配置。它们是“交付通道”,不自动提供审批、轮换和审计。

Terminal window
# 本地开发使用 user-secrets;值写入用户目录而非仓库。
dotnet user-secrets set \
--project src/Hosts/BitzOrcas.Api \
'Jwt:Secret' '<development-only-secret-at-least-32-chars>'
# 只列出键名用于排查,禁止在共享日志中输出真实值。
dotnet user-secrets list --project src/Hosts/BitzOrcas.Api

环境变量可能出现在容器描述、诊断快照或崩溃转储。Kubernetes Secret 也只是 API 对象,仍需要 etcd 加密、RBAC、审计和挂载权限。

JWT 密钥环

JWT 启动配置必须有 issuer、audience 和签名材料。没有 Jwt:SigningKeys 时,Jwt:Secret 至少 32 字符;缺失会直接启动失败。生产优先使用带 kid 的版本化 signing keys。

{
"Jwt": {
"Issuer": "bitzorcas-api",
"Audience": "bitzorcas-client",
"SigningKeys": {
"2026-07": "<secret-reference-or-injected-value>",
"2026-04": "<old-verification-key-during-overlap>"
},
"ActiveKeyId": "2026-07"
}
}

轮换顺序是先让所有验证方认识新 kid,再切换签发,等待旧 token 过期,最后撤销旧 key。直接覆盖单一 Secret 会让未过期 token 同时失效,可能造成全量登出。

HMAC、API Key 与 SCIM

HMAC 客户端需要可取回的 secret 来计算签名,因此 Secret Store 必须支持按 client id 定位与轮换。API Key 和 SCIM token 的服务端只需验证,当前运行态采用 SHA-256 hash 映射;管理面应只在创建时展示一次明文。

不要把 key prefix 当作安全材料。boa_live_ / boa_test_ 便于识别环境和审计,真正验证仍依赖完整 key 的 hash 与固定时间比较。

创建:CSPRNG 生成明文 → 向所有者展示一次 → 服务端保存 hash + prefix
调用:收到明文 → SHA-256 → 固定时间匹配 → 取得 tenant/scopes
撤销:禁用 hash 记录 → 后续请求立即失败

兼容性的静态配置适合开发和测试,不等于完整凭据目录。生产还需要 owner、过期时间、scope、最后使用、轮换重叠和撤销审计。

Webhook 与 Provider Secret

Webhook subscription secret 需要发送端取回,当前 repository 通过 IWebhookSecretCipher 加密后落库。Provider API Key 也可通过 Data Protection 做字段级可逆保护。

这类“数据库密文”仍依赖外层 key ring。一旦 key ring 丢失,密文无法恢复;一旦应用身份被攻破,攻击者可能通过正常解密路径取得 Secret。因此数据库备份与密钥环备份必须成对演练,应用权限也要最小化。

Data Protection key ring

API 可把 ASP.NET Core Data Protection key ring 持久化到 Redis 固定 key。Redis 凭据、网络 ACL 和 key-ring 数据本身都属于高敏感控制面。

多环境不可共享同一 key ring。多租户 SaaS 是否共享应用级 ring 是部署决策;即使共享,也必须用独立 purpose 和 tenant-aware 业务访问控制隔离用途。

详见 Data Protection 与共享密钥环。

Runtime License 材料

Production/Staging 的 API Runtime Guard 要求开启 signed runtime licensing,并配置 ProductId、ProductVersion、Environment、TenancyMode、DeploymentIdentityPath、CachePath 和至少一个 TrustedPublicKey。

Trusted public key 可进入受控配置仓库,但私有签发密钥必须留在许可证签发系统的 KMS/HSM 中,绝不能随产品部署。部署身份文件与离线缓存不是普通临时文件:它们需要稳定卷、最小权限和备份策略。

标准轮换流程

  1. 建立新版本,记录用途、owner、创建与失效时间;
  2. 验证消费者支持双 key 或多版本读取;
  3. 先部署验证新 key 的能力;
  4. 切换签发或主动连接到新版本;
  5. 观察认证失败、连接失败和旧版本命中;
  6. 等待最大 token、重试和离线作业窗口;
  7. 撤销旧版本,并证明旧值不能继续使用;
  8. 更新资产清单与演练记录。

数据库和 Broker 凭据更适合创建新账号、切流后撤销旧账号,避免原地改密造成同时中断。Webhook/JWT 可使用短暂双验证窗口,但窗口必须有终止时间。

应用读取模式

业务代码依赖 options 或抽象 Provider,不在 Handler 中直接读取环境变量。这样才能替换 Secret Store、测试轮换和集中脱敏。

// 组合根绑定配置并执行启动校验;Handler 只依赖能力接口。
builder.Services
.AddOptions<ConnectorOptions>()
.BindConfiguration("Connectors:Payments")
.ValidateDataAnnotations()
.ValidateOnStart();
// 日志只记录 provider/key version,不记录 SecretValue。
logger.LogInformation("Payment connector uses key version {KeyVersion}", options.KeyVersion);

CompositeSecretStore 的优先级与失败语义

基础设施按 DI 注入顺序把多个 ISecretStore 组成读取链,只遍历 IsAvailable=true 的来源。读取规则如下:

当前来源结果后续动作全链最终结果
成功立即返回,不再访问低优先级来源成功值
ErrorType.NotFound继续查低优先级来源所有可用来源都只报 NotFound 时,保留 SecretNotFound
其他失败记录 Warning,继续尝试后续来源后续无成功时返回 NoStoreAvailable
非取消、非 OOM 异常记录 Warning,继续尝试后续来源后续无成功时返回 NoStoreAvailable
没有可用来源无法读取NoStoreAvailable

只有“所有可用来源都确认没有该 Secret”才能返回 NotFound,调用方可据此使用明确的部署默认值。只要链中出现存储故障、未知失败或异常,最终就 fail closed,不能把基础设施故障伪装成“配置未提供”。OperationCanceledException 与 OutOfMemoryException 不会被吞掉。

RotateSecretAsync 也按优先级尝试可用来源,第一个成功即返回;当前写路径不会像读取那样保留 NotFound 分类,若没有来源成功,统一返回 NoStoreAvailable。不要假设读取回退来源就是轮换写入来源,部署清单必须明确哪个 Provider 拥有写权限。

泄露响应

推送到 Git、CI 日志、聊天、工单或公开镜像的 Secret 一律视为泄露。先撤销或轮换,再调查访问;删除消息或重写 Git 历史不能让旧值恢复安全。

响应步骤包括冻结受影响 key、查明时间窗、查询使用记录、轮换派生 token、通知 owner、修复暴露通道并补充检测。若 key 可以解密数据库字段,还要评估历史数据是否已经被读取。

扫描与防泄露

  • pre-commit 与 CI 执行 Secret pattern/entropy 扫描;
  • 构建产物、容器层和 source map 也纳入扫描;
  • ProblemDetails、结构化日志、审计详情和 metric label 做脱敏;
  • 配置诊断只输出键是否存在和来源,不输出值;
  • 测试 fixture 使用明确的 test prefix,不能复制生产 Secret;
  • 截图、录屏和支持包按敏感制品管理。

发布证据

每类 Secret 都要有资产记录、owner、读取主体、轮换周期、上次轮换、撤销方法和恢复演练。关键路径至少演练一次双 key 轮换和一次泄露撤销。

“启动成功”只能证明当前值可用,不能证明最小权限、轮换和审计。商业 GA 的证据应同时包含 Secret Store 策略、Provider 优先级与失败注入、工作负载身份、扫描结果、运行态脱敏和故障演练。

相关主题

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%