应使用 random_bytes() 生成密码学安全随机字符串,因其基于系统熵池;避免 rand()、mt_rand() 等非安全函数;推荐 bin2hex(random_bytes($length)) 转换为 url 安全十六进制字符串。

为什么不用 rand() 或 mt_rand() 生成随机字符串
因为它们不是密码学安全的——输出可预测,尤其在容器、低熵环境(如 Docker 启动初期)或被反复调用时,容易被暴力回推。JWT token、API key、重置密码链接里的随机码,一旦用错函数,等于把锁换成纸糊的。
常见错误现象:rand(0, 255) 拼接出的字符串,在某些 PHP-FPM 进程复用场景下会重复;md5(uniqid()) 也一样,uniqid() 依赖微秒时间戳,精度有限且无熵增强。
- 永远避开
rand()、mt_rand()、str_shuffle()、uniqid()做安全用途 -
openssl_random_pseudo_bytes()在 PHP 7.0+ 已废弃,别再抄老教程 - PHP 7.0+ 唯一推荐入口就是
random_bytes()
random_bytes() 怎么用才不出错
它返回的是二进制字节流,不是字符串——直接 echo 会乱码,直接 base64_encode 可能含 +、/ 和换行,不适用于 URL 或文件名场景。
使用场景:生成 API key、临时令牌、加密盐值(salt)、CSRF token。
- 先调用
random_bytes($length)获取原始字节,$length至少 16(对应 128 bit 安全强度) - 转成可用字符串时,优先选
bin2hex()(纯小写 a–f 0–9,URL 安全、易存储) - 若需更短字符串(比如 6 位验证码),用
base64_url_encode()自定义函数,或截取bin2hex()结果 - 不要用
base64_encode(),它产生的+、/、=在 URL 或 JSON 中要额外处理
示例(安全、简洁、可直接用):
function random_string_hex(int $length = 32): string {
return bin2hex(random_bytes($length));
}
// 生成 32 字符十六进制字符串,如:a1b2c3...
echo random_string_hex();
遇到 Exception: Could not gather sufficient random data 怎么办
这是 random_bytes() 在找不到足够熵源时抛的异常,多见于:旧版 Alpine Linux 容器、某些 OpenVZ 虚拟机、极简嵌入式 PHP 环境。
不是代码写错了,是系统层面缺随机数设备支持。
- 先确认 PHP 版本 ≥ 7.0 —— 低于这个版本压根没有
random_bytes() - 检查
/dev/urandom是否可读:php -r "var_dump(is_readable('/dev/urandom'));" - Alpine 用户装
haveged或升级到 edge 分支(glibc 改用 getrandom() 系统调用) - 绝对不要降级用
openssl_random_pseudo_bytes()并忽略$crypto_strong返回 false —— 那等于自欺欺人
如果必须兼容 PHP 5.6 怎么办
没有银弹。PHP 5.6 原生不提供密码学安全随机接口,mcrypt_create_iv() 已废弃且在 7.1 移除,openssl_random_pseudo_bytes() 是唯一勉强可用的,但必须校验 $crypto_strong。
- 必须显式判断返回值:
if (!$crypto_strong) { throw new Exception('Not cryptographically secure'); } - 不能只靠 try/catch —— 它不会抛异常,只是返回弱随机数据
- 线上环境务必测试该函数是否真返回
true,有些共享主机禁用了 OpenSSL 随机源 - 强烈建议升级 PHP 版本,PHP 5.6 已 EOL 超三年,连安全补丁都不再提供
真正难的从来不是写对一行 random_bytes(),而是确保从内核熵池、到 PHP 扩展、再到你的部署环境,整条链路没被悄悄阉割——这点,连很多 DevOps 都会漏看。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











