Token明文与哈希存储验证区别
两种做法的本质区别:数据库(连同备份、日志、快照)泄露后,攻击者拿到的是"能直接用的钥匙",还是"一堆没用的指纹"。
- 明文存放:签发 token → 原文入库 → 验证时
WHERE token = 传入值 - 哈希存放:签发 token →
hash(token)入库 → 验证时WHERE token_hash = hash(传入值)
后者里,明文 token 只在"发给用户那一次"存在,之后连系统自己都不知道原文是什么。
安全差异
泄露的爆炸半径
- 明文泄露 攻击者立刻能冒充任意用户,直到 token 过期
- 哈希泄露 拿到一堆无法反推的指纹
内部人风险
有 DB 读权限的运维、DBA、被攻陷的只读副本,都能直接盗用明文 token;哈希则挡住了这条路径。
意外泄露
明文 token 容易随着慢查询日志、错误上报、备份文件、内存 dump 流出去;哈希后这类事故的危害大幅降低。
和加密的比较
- token 哈希用 SHA-256 就够了,不需要 bcrypt/Argon2。 token 是高熵随机串(32 字节 CSPRNG),不存在"弱口令被字典/彩虹表打穿"的问题;慢哈希是为低熵密码设计的。只有当 token 很短或含可预测内容时才考虑慢哈希。
- 哈希后依然能精确查找、照常建索引,不需要遍历全表。 token 哈希是确定性的(不像密码 hash 带随机 salt),同样输入永远同样输出,查询性能和明文几乎没差别。
- 不要用"加密"代替哈希。 加密要额外管密钥;哈希是单向的,更契合"服务端本来也不需要原文"这个事实。
什么时候用哈希
- 建议一律哈希:refresh token、长效 session、API key、密码重置 / 邮箱验证 token、OAuth 授权码——凡是"长期有效 + 高权限 + 落库"的。
- 可以不哈希:几分钟就过期且带尝试次数限制的一次性验证码;以及服务端根本不存储的无状态 JWT(压根不入库,谈不上存明文)。
使用哈希存储token的代价
- 无法直接去数据库查看/调试某个 token,排查线上问题略麻烦
- 无法"重发同一个旧 token",只能重新签发
- 存量数据迁移时要重新哈希一遍
- 多一次哈希计算的 CPU 开销,可忽略