Secrets 管理四大原则:最小权限、轮换、审计、加密存储
说实话,我早期管 secrets 的时候,踩的坑十个手指头数不过来。后来我慢慢摸出规律——其实 secrets 管理就四个核心原则。你把这四条咬死了,至少 80% 的事故能提前挡住。下面我一条一条掰开讲,你对照自己的项目看看,哪条还没做到位。
原则一:最小权限(Least Privilege)
最小权限,说白了就是“够用就行”。别给一个只读数据库的 配上 delete 权限,更别把生产环境的密钥塞到所有人能拉的 CI 变量里。我自己的习惯是:每个 secret 只绑定一个具体的操作范围,而且不搞永久授权。
实现最小权限时,常用的手段有:
- 角色分离:读写分离、管理 vs 使用分离。比如数据库连接密码只给 SELECT 权限,写操作走独立的服务账号。
- 临时凭证:用 STS / 短期 ,过期就废。尽量避免长期静态密钥。
- 动态授权:Vault 的 Dynamic Secrets 或 AWS Secrets Manager 的轮换策略,每次获取都生成一次性的临时凭证。
原则二:轮换(Rotation)
密钥不轮换,就像家里的锁一直不换。你想想看,就算当初分发安全,半年后谁知道 key 的副本流落到哪里了?轮换的核心就一句话:自动化、无感知、频率可控。
我见过一个团队手动改数据库密码,改完后才发现有 6 个服务还在用旧密码,整整断连了 40 分钟。所以轮换不能靠人肉,必须靠系统。
| 密钥类型 | 建议轮换周期 | 轮换方式 |
|---|---|---|
| 数据库密码 | 30 天 | 双版本 + 渐变切换 |
| API 令牌 | 7 天 或 按会话 | 自动生成 + 旧令牌延迟删除 |
| SSH 密钥对 | 90 天 | 托管 CA,自动签新证 |
| JWT 签名密钥 | 按发行策略(通常 1h–24h) | 密钥集 + kid 指向 |
原则三:审计(Audit)
审计不是用来事后甩锅的,而是用来发现“异常访问模式”。我自己的信条是:每一次 secrets 的读取、写入、删除都必须留下不可篡改的日志。
为什么要这样做?因为你根本不知道哪个开发者偷偷 copy 了线上数据库密钥。审计日志就是你的摄像头。我记得有一次排查泄漏事故,全靠 Vault 的 audit log 定位到具体是哪个 CI Job 在哪个 commit 里拉取过敏感 secret。
- 日志必须集中存储:别散落在各个节点上。我一般用 ELK 或 Splunk,开启
audit_mode=strict。 - 做告警基线:比如“一个服务每分钟拉取同一个 secret 超过 10 次”就报 alert。这往往意味着被暴力遍历或代码写死了重试。
- 保留至少 90 天:合规要求有的行业要保留 1 年。注意日志不能回写。
原则四:加密存储(Encryption at Rest & in Transit)
这点最直观,但也最容易忽视细节。加密存储包含两层:传输层和存储层。
传输层——所有 secrets 的读写必须走 TLS 1.2+。我见过有人用 HTTP 从本地直接读取远程 vault,理由是内网安全……内网就没风险吗?内网被横向移动后,明文流量等于裸奔。
存储层——我建议至少使用 AES-256-GCM。而且密钥本身不能明文落盘。要用 KMS 或 HSM 来 wrap 加密密钥,形成“密钥链”。具体做法:数据用 DEK(数据加密密钥)加密,DEK 用 KEK(密钥加密密钥)加密,KEK 存在 KMS 里。应用拿到的永远是密文,想要解密必须过 KMS 的权限检查。
import os, struct
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
def encrypt(data: bytes, key: bytes) -> bytes:
iv = os.urandom(12) # 随机 IV
cipher = Cipher(algorithms.AES(key), modes.GCM(iv))
encryptor = cipher.encryptor()
ct = encryptor.update(data) + encryptor.finalize()
# 返回 iv + ciphertext + tag
return iv + ct + encryptor.tag
def decrypt(payload: bytes, key: bytes) -> bytes:
iv, ct_tag = payload[:12], payload[12:]
ct, tag = ct_tag[:-16], ct_tag[-16:]
cipher = Cipher(algorithms.AES(key), modes.GCM(iv, tag))
decryptor = cipher.decryptor()
return decryptor.update(ct) + decryptor.finalize()
你注意看上面代码里,我没有把密钥管理写在里面。因为真正生产环境下,key 是从 KMS 或 Vault 的 transit 引擎获取的,永远不直接暴露给应用进程。
四条原则的关系
这四个原则其实是环环相扣的:最小权限减少暴露面,轮换缩短被攻击窗口,审计保证可见性,加密存储守住最后一道防线。缺一条,其他三条的效果就打折扣。
这四个原则你只要真理解了,写出来的 secrets 管理方案就不会飘。我自己的代码库里一直挂着这四条,每次架构评审先拿原则过一遍:有没有不必要的权限?密钥多久轮一次?审计日志能回溯到哪个粒度?加密算法和密钥链路对不对?全过了,才能上线。