Archived
refactor: session 改用 itsdangerous,去掉手写密码学原语
为什么:core/session.py 原本手写 HMAC + 自定义 base64 签名,等于自己复刻 itsdangerous 的核心功能——密码学原语自己实现是风险点,且难与上游的安全 修复同步。 关键约束(换库的坑):两者字节级格式并不一致——手写实现用标准 base64 且 保留 '=' 填充,itsdangerous 用 urlsafe 且去填充。若不钉住旧格式,部署瞬间 线上全部登录态会失效。 改法: - 用 URLSafeTimedSerializer,按 secret 做 lru_cache(maxsize=8),避免每请求 重建序列化器 - loads() 增加 max_age 参数(默认 None,保持既有「不叠加服务端过期」行为) - get_uid() 顺带加固:cookie 里塞非整数不再抛 ValueError 变 500,改为返回 None - 删除 _derive_key / _int_to_bytes 及手写 HMAC/b64 验证:新增 2 例回归测试,其中 legacy cookie 样本由替换前的旧实现在固定时间戳 1700000000 下采集,钉住的是格式而非实现;另一例验证 max_age=None 接受旧 时间戳、max_age=60 拒绝。
This commit is contained in:
1 parent
0b25f215a2
commit
2b4704183c
2 files changed
+63
-47
No files matched your search
@@ -65,6 +65,30 @@ def test_roundtrip_selfconsistency():
|
||||
assert cs.loads(SECRET, cs.dumps(SECRET, payload)) == payload
|
||||
|
||||
|
||||
def test_legacy_handwritten_cookie_still_loads():
|
||||
"""旧手写实现签发的 Cookie 仍能被解析(存量会话不必重新登录)。
|
||||
|
||||
样本由「替换 itsdangerous 之前」的 core.session.dumps 生成,时间戳固定为
|
||||
1700000000,故字符串永久有效。它钉住的是**格式**而非实现:旧实现的
|
||||
payload 用标准 base64 且保留 `=` 填充,签名同理;itsdangerous 用 urlsafe
|
||||
且去掉填充——两侧必须继续互通,否则线上所有登录态会在部署瞬间失效。
|
||||
"""
|
||||
legacy = ('eyJzaXRlX3VpZCI6MjYsInNpdGVfdWlkX3NlcSI6NX0='
|
||||
'.ZVPxAA.hW2ZBQ0BL1OhI4D4eDKFFVvqGO0')
|
||||
data = cs.loads(SECRET, legacy)
|
||||
assert data == {'site_uid': 26, 'site_uid_seq': 5}
|
||||
assert cs.get_uid(data) == 26
|
||||
|
||||
|
||||
def test_max_age_none_accepts_old_timestamp():
|
||||
"""默认不校验签发时间(max_age=None)——与既有行为一致,旧 Cookie 不过期。"""
|
||||
legacy = ('eyJzaXRlX3VpZCI6MjYsInNpdGVfdWlkX3NlcSI6NX0='
|
||||
'.ZVPxAA.hW2ZBQ0BL1OhI4D4eDKFFVvqGO0')
|
||||
assert cs.loads(SECRET, legacy) is not None
|
||||
# 显式给出 max_age 时,超龄的旧 Cookie 必须被拒绝(供未来启用过期用)
|
||||
assert cs.loads(SECRET, legacy, max_age=60) is None
|
||||
|
||||
|
||||
def test_get_uid_none():
|
||||
assert cs.get_uid(None) is None
|
||||
assert cs.get_uid({}) is None
|
||||
|
||||
Reference in new issue
Block a user