必须用 random_bytes() 生成密码学安全随机字符串,禁用 rand()、mt_rand()、uniqid() 等非安全函数;ThinkPHP 默认已用 random_bytes(),但手动实现时须调用 bin2hex(random_bytes(32)) 确保不可预测性。

ThinkPHP 的 _token 或验证码 Token 一旦用弱随机源(如 rand()、mt_rand()、uniqid())生成,攻击者可在几秒内暴力穷举或预测出有效值,等同于把门锁换成纸糊的——不是“防不住”,而是“根本没锁”。
为什么 rand() 和 uniqid() 不行
这些函数不满足密码学安全要求:
-
rand()和mt_rand()输出可被状态反推:PHP 的 Mersenne Twister 算法在已知若干输出后,能重建内部状态并预测后续所有值 -
uniqid()本质是微秒时间戳 + 可选 prefix,只要知道服务器大致时间(误差 ±2 秒内),就能生成上万候选值批量碰撞 - 它们都不依赖操作系统熵池,无法抵抗定时侧信道攻击或容器环境下的熵不足问题
ThinkPHP 实际用什么才算强随机
框架自身在 TokenBuild::build() 中已默认使用 random_bytes()(PHP 7.0+),但你手动生成 Token 时必须主动调用它:
- 正确写法:
bin2hex(random_bytes(32))—— 生成 64 位十六进制字符串,不可预测、不可重现 - 错误写法:
md5(uniqid())、sha1(rand().time())、base64_encode(time().mt_rand()) - TP6 验证码扩展中,
Captcha类底层也依赖random_bytes()生成字符序列;若你重写buildImage(),务必沿用该函数
部署环境里容易被忽略的熵陷阱
即使写了 random_bytes(),也可能因系统配置失效:
立即学习“PHP免费学习笔记(深入)”;
- Docker 容器未挂载
/dev/urandom或未启用getrandom()系统调用,会导致random_bytes()抛出Exception(而非静默降级) - 某些旧版 PHP(openssl_random_pseudo_bytes(),而后者在 OpenSSL 初始化失败时会返回弱随机值
- TP 的
session_start()若被手动调用(禁用中间件时),会绕过框架对random_bytes()的封装,导致 session ID 本身就不安全
真正起作用的从来不是“有没有 Token”,而是“Token 是否真不可预测”——哪怕只有一处用了 rand(),整条链路就塌了。



















