PHP 5.x 必须用 random_compat 替代 random_int(),因其原生不支持该函数且 mt_rand() 等替代方案存在可预测性缺陷;random_compat 通过自动降级机制提供与 PHP 7+ 一致的安全随机数。

PHP 5.x 必须用 random_compat 替代 random_int()
PHP 5.2–5.6 原生不支持 random_int(),直接调用会报 Fatal error: Call to undefined function random_int()。这不是版本升级就能解决的缺失,而是底层随机源抽象尚未引入。你不能靠改写逻辑绕过——比如用 mt_rand() 加时间戳拼接,这种组合依然可预测,等同于没加固。
唯一被广泛验证、持续维护、且与 PHP 7+ 行为一致的方案是引入 paragonie/random_compat 库:
- 通过 Composer 安装:
composer require paragonie/random_compat - 确保自动加载生效(
require_once 'vendor/autoload.php')后,random_int(1, 100)就能照常使用 - 它在不同环境自动降级:优先走
/dev/urandom,fallback 到openssl_random_pseudo_bytes(),再不行才尝试mcrypt_create_iv()(已废弃但兼容旧容器)
别碰 openssl_random_pseudo_bytes() 单独用
有人查到 PHP 5.3+ 有 openssl_random_pseudo_bytes(),就以为能“手写安全替代”。它确实比 mt_rand() 强,但存在两个硬伤:
- 返回值第二个参数是
$crypto_strongbool 引用,你必须显式检查它是否为true,否则可能拿到伪随机数据却误以为安全 - 它只生成字节串,要转整数还得自己做模运算(如
unpack('V', openssl_random_pseudo_bytes(4))[1]),而模运算若不处理边界会导致分布偏差(bias) - 某些 Docker Alpine 镜像或精简版 PHP 编译时没启用 OpenSSL,运行时直接失败,且无 fallback
换句话说:openssl_random_pseudo_bytes() 是个半成品工具,random_compat 才是把它用对的完整封装。
立即学习“PHP免费学习笔记(深入)”;
PHP 7.0+ 还在用 mt_rand()?立刻停用
即使你已升级到 PHP 7 或更高版本,只要代码里还留着 mt_rand(),就等于在登录页门口挂了把玩具锁。它的 Mersenne Twister 状态仅需 624 个输出即可完全还原——而很多系统会把验证码、密码重置 token、CSRF nonce 直接暴露在 HTTP 响应头或日志里。
-
mt_rand()在 PHP 8.3 已被正式标记为 deprecated,PHP 8.4 很可能彻底移除 -
random_int()不接受浮点参数,所以mt_rand(0.1, 0.9)这类写法换成random_int()会直接 fatal error,必须重构逻辑 - 不要给
random_int()包裹@抑制错误——它失败时抛的是Exception,不是 warning,抑制只会让服务静默崩溃
生成字符串 token 别硬凑 random_int()
常见错误:用 random_int(100000, 999999) 当六位验证码,或拿 random_int(0, 35) 查表拼字母数字。这看似方便,实则埋雷:
- 数字范围固定 → 总共只有 90 万种可能,暴力枚举成本极低
- 字符集映射若用简单数组索引(如
$chars[$i % strlen($chars)]),当random_int()返回值远大于字符集长度时,末尾字符出现概率偏高 - 真正安全的做法是:先用
random_bytes(16)拿 16 字节强随机数据,再用bin2hex()或base64_encode()转成字符串;需要定长可截取,需要字母数字可用strtr()替换 base64 中的+/
所有看似“够用”的捷径,都在悄悄降低攻击者的成本。安全随机数不是功能开关,而是每个字节都得经得起熵源审计的基础设施。



















