四、令牌生成策略:随机、哈希与加密,谁更安全?
生成令牌的方式,说白了就三种:随机生成、哈希生成、加密生成。每种都有它的用武之地,也各有各的坑。
我个人习惯在项目初期就把策略定死,因为后面再改,代价挺大的。今天我们就掰开了聊聊,这三种策略到底怎么选。
4.1 随机生成——最简单,也最容易被忽略
随机生成,就是直接从熵池里抓一串随机字节,然后编码成字符串。比如 UUID v4、SecureRandom 出来的 32 字节,都可以。
核心逻辑:令牌本身就是值,不是算出来的。
// 伪代码示例:随机令牌生成
byte[] randomBytes = SecureRandom.getBytes(32);
String = Base64URL.encode(randomBytes);
// 输出类似:a3F8zT9kLp2qR7vX4mN6cB0jH5wY1sD8
我曾在一个支付项目中见过,团队直接用 Math.random() 拼字符串——结果可想而知, 很快就被撞破了。
- 优点:实现快,性能高,不需要额外密钥。
- 缺点:无法追溯源数据,无法验证令牌是否被篡改。
我建议:一定要用加密级随机数生成器(如 Java 的 SecureRandom、Node.js 的 crypto.randomBytes)。普通随机数,千万别碰。
4.2 哈希生成——可重复,但别想逆行
哈希生成,就是对原始标识(比如用户 ID + 时间戳)做哈希。同一输入,永远得到同一令牌。
// 伪代码:哈希令牌生成
String raw = userId + "|" + timestamp + "|" + secret;
String = SHA256.hash(raw);
// 输出:d2e4f6a8b0c1...(固定长度)
这玩意适合什么场景?我举个例子:设备指纹。同一设备每次请求, 都该一样。哈希正好满足。
但注意,哈希是单向的。一旦你存了哈希后的 ,就再也拿不回原始数据了。而且,彩虹表攻击、碰撞攻击都是真实存在的威胁。
我曾经踩过坑:有个项目用 MD5 做令牌哈希,后来发现攻击者用彩虹表10分钟就逆出了用户ID。MD5、SHA-1,原则上别再用。
4.3 加密生成——可逆,但要管好密钥
加密生成,就是先把数据加密,再把密文当令牌用。接收方拿到令牌后,解密就能还原原始信息。
// 伪代码:加密令牌生成
String plainText = userId + "|" + expireTime;
byte[] cipherText = AES.encrypt(plainText, secretKey);
String = Base64URL.encode(cipherText);
// 解密后得到原始数据
它比哈希多了一个好处:你能知道 里藏了什么。比如,用户 ID、权限等级、过期时间,全塞进去,服务端不用查数据库就能验证。
但代价也很明显:密钥管理是个大麻烦。密钥泄露,令牌全部作废。我见过一个团队把密钥硬编码在代码库里,还提交到了 GitHub——嗯,两天后他们就出事。
| 特性 | 随机生成 | 哈希生成 | 加密生成 |
|---|---|---|---|
| 可逆性 | 不可逆 | 不可逆 | 可逆(需密钥) |
| 重复性 | 不可重复 | 可重复 | 可重复(相同明文) |
| 性能 | 最快 | 较快 | 较慢 |
| 依赖 | 无 | 无密钥 | 需密钥管理 |
| 安全风险 | 碰撞概率极低 | 彩虹表、碰撞攻击 | 密钥泄露风险 |
| 适用场景 | 会话令牌、一次性票据 | 设备指纹、去重标识 | 有状态的令牌、授权码 |
4.4 一张图看懂三种策略的差异
下面这张图,我画出了三种策略的核心流程。你看一眼,基本就清楚了。
4.5 安全比较——到底该信谁?
你可能会问,这三种策略,到底哪种最安全?
答案是:看上下文。
- 如果令牌只用一次,用完就丢:随机生成就够了。你想想看,一个 32 字节的随机数,攻击者想猜出来,比中彩票还难。
- 如果你需要反复验证同一身份:哈希生成更合适。但一定要加盐,不加盐等于裸奔。
- 如果你需要在令牌里藏信息:加密生成是唯一选择。但密钥管理这块,我建议用独立的密钥管理系统,别自己手搓。
我之前审计过一个系统:他们用 AES-ECB 模式加密令牌,结果相同前缀的令牌总是生成相同的前半段——攻击者直接通过模式匹配猜出了用户等级。ECB 模式,建议永远不要用。
来说一个实在的避坑指南:
- 别偷懒用内置随机函数。 Unity 的
Random.Range、PHP 的rand()都算普通随机数,不适合令牌。 - 哈希令牌一定要加盐。 不加盐的话,两个相同用户会生成相同令牌,攻击者一看就知道。
- 加密令牌要加签名。 我用的是 AES-GCM 模式,它自带认证,能防篡改。
- 定期轮换密钥。 我有个习惯,三个月换一次密钥,而且旧密钥保留一个周期用于解密已有令牌。
记住一句话:随机生成防猜测,哈希生成防逆行,加密生成防偷窥。没有银弹,只有匹配场景才是好策略。