安全相关的随机数:为什么不能直接用 rand()
用 rand() 生成密码重置 token,攻击者可以通过种子推算出下一个值——这类漏洞在 CTF 和真实系统里都常见。
安全场景必须用密码学安全随机数(CSPRNG),区别在于「不可预测」。
先说结论
token 用 CSPRNG 生成 128 位以上,编码成 URL 安全字符串。
- Python 用 secrets,Java 用 SecureRandom,PHP 用 random_bytes
- UUIDv4 可用于标识,不用于高价值 token
- token 要有有效期与一次性
- 不要用时间戳/自增 ID 拼随机
漏洞是怎么产生的
普通 PRNG 的目标是「分布均匀」,CSPRNG 的目标是「不可预测」。前者可被反推,后者不行。
长度也重要:64 位随机在生日碰撞下并不够,安全 token 建议 128 位以上。
修复与验证
修复不是改一行代码,而是四件事一起做:
- 盘点 token 生成点:会话、重置密码、邀请、API Key
- 改用语言标准库的 CSPRNG
- 长度 ≥ 128 位,URL 安全编码
- 设置有效期与使用次数限制
- 存储时哈希,避免库泄露即失守
攻击面与检测点
按影响面排序,先修影响最大的:
| 语言 | 正确 API | 错误用法 |
|---|---|---|
| Python | secrets.token_urlsafe | random.random |
| PHP | random_bytes / random_int | rand / mt_rand |
| Java | SecureRandom | java.util.Random |
| Go | crypto/rand | math/rand |

几个容易踩的坑
下面这些是最常见的错误做法:
- 用时间戳做种子:可枚举
- token 太短:可暴力枚举
- token 长期有效:泄露即长期可用
- 把 token 明文存库:拖库后可冒用
写在最后
安全随机数的判断标准只有一个:拿到一批输出,能不能预测下一个。
安全不是一次加固,是持续的假设:假设输入不可信、假设边界会破、假设有人会看错。
评论 (3)
修复清单比漏洞描述有用多了,很多文章只讲怎么打不讲怎么防。
白名单那条永远是最有效的,正则黑名单迟早绕过。
建议再补一条:把这类检查加到 CI 里,靠人记必然漏。
发表评论