深入理解 MD5 + 时间戳的防篡改/防重放原理。
# 签名生成的核心逻辑
def sign(act, card, mac, app, ts, app_key):
"""
1. 拼接字符串: act|card|mac|app|ts|app_key
2. MD5 摘要
3. 转 32 位小写 hex
"""
raw = f"{act}|{card}|{mac}|{app}|{ts}|{app_key}"
return hashlib.md5(raw.encode("utf-8")).hexdigest()
# === 为什么能防篡改? ===
# - 攻击者拿到一个成功的请求后,修改 card/mac 是无效的,
# 因为 server 会用同样的拼接顺序算 MD5,签名对不上。
# - 即使攻击者知道 app_key,也无法伪造历史请求(见下)。
# === 为什么能防重放? ===
# - 客户端每次请求都带 ts(Unix 秒)。
# - server 校验 |now - ts| <= 300,超出直接拒绝签名错误。
# - 攻击者抓包后重放,最多能重放 5 分钟内的请求,
# 且 server 可以加 nonce/replay-token 机制升级为 0 重放窗口。
# === 为什么用 MD5 而不是 SHA256? ===
# - 兼容 keyt.cn 旧版客户端。
# - 签名本身只是"完整性校验",加密靠 HTTPS。
# - 若需升级,server 端可同时支持 MD5 与 HMAC-SHA256。
# === 客户端如何隐藏 app_key? ===
# 推荐做法:
# 1. 将 app_key 拆分成多段字符串,运行时拼接。
# 2. 用 PyInstaller --key 加密 .pyc,逆向门槛大幅提高。
# 3. 配合 HTTPS + 证书绑定(防中间人)。
# 4. 关键校验逻辑放到服务器端,客户端只展示结果。