4、认证:设计原则、生成与分发、密钥管理最佳实践
这东西,说白了就是一把“钥匙”。你得把它设计好、发出去、还要管得住。我早年接手过一个遗留系统,密钥竟然是自增的数字,从 10001 开始。你想想看,攻击者随便扫一下就能猜到别人的 Key。嗯,从那以后我对密钥设计就特别敏感了。
4.1 设计原则:好的 长什么样?
一个好的 ,不是一串随机字符就完事了。我个人习惯从三个维度去考量:不可预测性、可识别性、可撤销性。
- 熵值要够高:至少 128 位随机性,通常用 256 位。我见过有些系统用 MD5(时间戳) 来生成 Key,结果被人几分钟就碰撞出来了。
- 带前缀标识:比如
sk_live_表示生产环境,sk_test_表示测试环境。这样一眼就能看出用途,排查问题特别方便。 - 包含校验位:类似信用卡号的 Luhn 算法,防止手误输错。我们曾经因为一个字符输错,排查了整整一下午。
核心公式: = 固定前缀 + 随机字符串 + 校验位
随机部分推荐使用 os.urandom() 或 secrets._hex(),别用 random 模块,那个不够安全。
4.2 生成与分发:怎么造?怎么给?
生成这块我直接上代码,你拿回去就能用。记得我在一个金融项目里就是用的这套方案,线上跑了三年没出过问题。
import secrets
import hashlib
def generate_(prefix="sk_live"):
"""生成 和对应的哈希值"""
raw_key = secrets._hex(32) # 64 个十六进制字符,256 位熵
= f"{prefix}_{raw_key}"
# 存储时只存哈希,不存原始 Key
hashed = hashlib.sha256(.encode()).hexdigest()
return , hashed
# 使用示例
key, hash_value = generate_()
print(f"分发给用户:{key}")
print(f"存入数据库:{hash_value}")
分发这块,我建议走「即时生成,一次性展示」的流程。用户创建 Key 的时候,页面只展示一次原始值。一旦刷新页面,原始值就再也看不到了。这样做的好处是,即使数据库被拖库,攻击者也拿不到原始 Key。
注意:千万不要在日志里打印完整的 !我曾经排查过一个事故,就是因为开发同学在调试日志里打印了 request.headers['X-'],结果日志文件被泄露,所有 Key 都得重新生成。
4.3 密钥管理最佳实践:让 Key 活着,但别乱跑
密钥管理是个细活。我把它拆成几个关键环节,你看看自己项目里有没有踩过类似的坑。
| 管理环节 | 最佳实践 | 常见误区 |
|---|---|---|
| 存储 | 只存 SHA-256 哈希值,原始 Key 不落库 | 直接把 Key 明文存数据库 |
| 传输 | 强制使用 HTTPS,Key 放在 Header 里 | 放在 URL 参数中,会被浏览器记录历史 |
| 轮转 | 每 90 天强制轮转,支持双 Key 缓冲期 | 一年不换,或者换了直接全部失效 |
| 吊销 | 提供即时吊销接口,支持按用户/按 Key 吊销 | 只能吊销全部 Key,或者吊销后还有缓存残留 |
| 审计 | 记录每次 Key 的创建、使用、吊销日志 | 完全不记日志,出问题没法追溯 |
小技巧:双 Key 缓冲期是一个很实用的模式。用户要换 Key 的时候,新旧两个 Key 同时生效 24 小时。这样用户可以在不停机的情况下完成迁移。等确认旧 Key 没人用了,再把它吊销掉。
4.4 签名算法:不只是传一个 Key 那么简单
很多同学以为 认证就是传个字符串,其实还有更安全的玩法。我参与过一个开放平台的项目,我们用了 HMAC 签名的方式,效果很好。
import hmac
import hashlib
import time
def sign_request(api_secret: str, method: str, path: str, timestamp: int, body: str = "") -> str:
"""生成签名,防止请求被篡改"""
message = f"{method}\n{path}\n{timestamp}\n{body}"
signature = hmac.new(
api_secret.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
return signature
# 服务端验证入口
def verify_request(: str, signature: str, timestamp: int, ...) -> bool:
# 1. 用 查到对应的 api_secret
# 2. 用同样的算法重新计算签名
# 3. 比较两个签名是否一致
# 4. 检查 timestamp 是否在 5 分钟内(防重放攻击)
pass
为什么会这么设计?因为只传 的话,只要 Key 被人截获,攻击者就能冒充你。而带上签名之后,即使 Key 泄露了,攻击者不知道 Secret 也签不出来正确的值。
4.5 生命周期全景图
我把整个生命周期整理成了一张图,你看看就清楚了:
我个人的工程经验:别想着一步到位。先把生成、存储、吊销这三个基础环节做扎实,再逐步加上签名和轮转。我曾经见过一个团队,一上来就搞复杂的签名算法,结果密钥都存不明白,反而更危险。
好, 认证的核心内容就这些。设计原则是基础,生成分发是流程,签名验签是升级,生命周期管理是保障。把这几块串起来,你的 API 安全就有了第一道防线。