mt_rand() 不安全,因其基于Mersenne Twister算法,仅需624个输出即可预测全部随机数;应改用random_int()生成加密安全整数,或random_bytes()配合编码生成字符串。

为什么 mt_rand() 不该再用了
它不是加密安全的,生成的随机数能被预测——哪怕你只是用来做密码重置链接里的 token,也够攻击者暴力猜出几百个。PHP 7.0 起官方就明确标记 mt_rand() 为“不适用于密码学场景”,但很多人还在用它生成验证码、临时密钥、盐值。
根本问题在于:它基于 Mersenne Twister 算法,速度快、分布均匀,但内部状态只有 624 个整数,只要拿到连续 624 个输出,就能完全还原种子并预测后续所有值。线上服务一旦暴露几个 mt_rand() 结果(比如 API 返回的 nonce),风险就实打实存在。
random_int() 怎么用才对
它是 PHP 唯一推荐的加密安全整数生成函数,直接从操作系统熵源(/dev/urandom 或 CryptGenRandom)读取,不可预测、无状态、无需手动播种。
- 只接受两个参数:
random_int($min, $max),且必须是整数,不能传浮点或字符串 - 范围包含两端,
random_int(1, 6)可能返回 1 或 6 - 失败时抛出
Exception(不是返回 false),必须 try/catch,否则进程崩掉 - 不要用它生成大范围整数来“模拟”字符串——比如
random_int(0, 999999)当验证码,位数固定易被枚举;真要字符串,得配合random_bytes()+ 编码
示例:
立即学习“PHP免费学习笔记(深入)”;
try {
$token = random_int(100000, 999999);
} catch (Exception $e) {
// 记日志,别裸 throw
error_log('random_int failed: ' . $e->getMessage());
die('Service unavailable');
}
替代 mt_rand() 时的典型翻车点
很多人以为把 mt_rand(1, 100) 换成 random_int(1, 100) 就完事了,结果上线后报错或逻辑异常。
-
random_int()不支持 float 参数,mt_rand(0.1, 0.9)这种写法会直接 fatal error - 旧代码里用
mt_srand()强制设种子?删掉。random_int()完全无视它,留着反而误导维护者 - 批量生成大量随机数?别循环调用
random_int()。每次调用都走系统调用,性能差。改用random_bytes($n)一次读够,再按需解析 - PHP 版本低于 7.0?别硬套。要么升级,要么用
openssl_random_pseudo_bytes()(注意检查$crypto_strong返回值)
生成随机字符串别绕弯子
想生成 8 位字母数字 token?别用 random_int() 拼接字符表——效率低、可能引入偏差(比如字符表长度非 2 的幂时,取模会偏斜)。
正确姿势是两步:
- 先用
random_bytes(8)拿 8 字节强随机数据 - 再用
bin2hex()或base64_encode()编码,最后用strtr()去掉+//(如果 base64)或截断长度
示例(URL 安全 token):
$bytes = random_bytes(8); $token = rtrim(strtr(base64_encode($bytes), '+/', '-_'), '=');
这比循环 8 次 random_int() 快 3 倍以上,且无分布偏差。
真正难的从来不是“换一个函数”,而是确认整个随机数使用链路——从生成、传输、存储到校验——没一处偷偷降级成 mt_rand() 或时间戳拼接。



















