php 7.2 中 shuffle() 因依赖全局 mt19937 状态而不可预测,应改用 random\randomizer(secure) 或 random_int() 实现 fisher-yates 洗牌以保障安全性。

PHP 7.2 中 shuffle() 本身不直接暴露随机种子,但它的底层依赖全局 Mt19937 状态,而该状态可被 mt_srand() 或 srand() 显式重置——一旦用已知值播种,后续所有 shuffle() 输出就完全可预测。这不是“低版本特有”,而是 PHP 7.2 及之前长期存在的设计事实,且在 7.2 中仍未移除该风险路径。
shuffle 的随机性来源不可控
shuffle() 不接受自定义随机器,强制复用全局梅森旋转实例。这意味着:
- 任何地方调用
mt_srand(12345),都会让之后所有shuffle()调用产生相同序列 - 多个模块共用同一进程(如 Web 请求)时,一个模块调用
mt_srand()会污染其他模块的打乱结果 - 即使没手动播种,PHP 启动时默认用时间+进程ID初始化,但在短周期高频请求中仍可能碰撞出相似序列
安全敏感场景必须绕过 shuffle()
若用于抽奖、权限分配、临时令牌生成等需“不可预测”行为的逻辑,shuffle() 不满足要求。应改用加密安全方案:
- 使用
Random\Randomizer配合Secure引擎:$r = new Random\Randomizer(new Random\Engine\Secure());<br>$shuffled = $r->shuffleArray($array);
- 对简单数组,也可先用
random_int()实现 Fisher-Yates 手动洗牌,确保每步都基于 CSPRNG - 避免混用
rand()、mt_rand()、str_shuffle()等共享同一全局状态的函数
兼容旧代码的平滑过渡建议
不能立刻升级到 PHP 8+?可在 PHP 7.2 中封装替代函数:
- 写一个
secure_shuffle(),内部用random_int(0, $i)实现 Fisher-Yates,不依赖全局状态 - 禁用
mt_srand()和srand():在入口文件加ini_set('precision', 0);无用,真正有效的是运行时拦截——可通过disable_functions配置项禁用这两个函数 - 静态分析时重点关注是否出现
mt_srand(或srand(字样,尤其在框架初始化或配置加载阶段
本质上不是版本高低的问题,而是是否把“随机”当成黑盒来用。只要依赖全局状态,就存在被观测、被复现、被操控的可能。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











