PHP中的随机性 你觉得自己幸运吗?


Posted in PHP onJanuary 22, 2016

本文分析了生成用于加密的随机数的相关问题。 PHP 5没有提供一种简单的机制来生成密码学上强壮的随机数,但是PHP 7通过引入几个CSPRNG函数来解决了这个问题。

PHP中的随机性 你觉得自己幸运吗?

一、什么是CSPRNG

引用维基百科,一个密码学上安全的伪随机数发生器(Cryptographically Secure Pseudorandom Number Generator 缩写CSPRNG)是一个伪随机数生成器(PRNG),其生成的伪随机数适用于密码学算法。

CSPRNG可能主要用于:

  • 密钥生成(例如,生成复杂的密钥)
  • 为新用户产生随机的密码
  • 加密系统

获得高级别安全性的一个关键方面就是高品质的随机性

二、PHP7 中的CSPRNG

PHP 7引入了两个新函数可以用来实现CSPRNG: random_bytes 和 random_int。

random_bytes 函数返回一个字符串,接受一个int型入参代表返回结果的字节数。

例子:

$bytes = random_bytes('10');
var_dump(bin2hex($bytes));
//possible ouput: string(20) "7dfab0af960d359388e6"

random_int 函数返回一个指定范围内的int型数字。

例子:

var_dump(random_int(1, 100));
//possible output: 27

三、后台运行环境

以上函数的随机性不同的取决于环境:

  • 在window上,CryptGenRandom()总是被使用。
  • 在其他平台,arc4random_buf()如果可用会被使用(在BSD系列或者具有libbsd的系统上成立)
  • 以上都不成立的话,一个linux系统调用getrandom(2)会被使用。
  • 如果还不行,/dev/urandom 会被作为最后一个可使用的工具
  • 如果以上都不行,系统会抛出错误

四、一个简单的测试

一个好的随机数生成系统保证合适的产生“质量”。为了检查这个质量, 通常要执行一连串的统计测试。不需要深入研究复杂的统计主题,比较一个已知的行为和数字生成器的结果可以帮助质量评价。

一个简单的测试是骰子游戏。假设掷1个骰子1次得到结果为6的概率是1/6,那么如果我同时掷3个骰子100次,得到的结果粗略如下:

0 个6 = 57.9 次
1 个6 = 34.7次
2 个6 = 6.9次
3 个6 = 0.5次
以下是是实现实现掷骰子1,000,000次的代码:

$times = 1000000;
$result = [];
for ($i=0; $i<$times; $i++){
  $dieRoll = array(6 => 0); //initializes just the six counting to zero
  $dieRoll[roll()] += 1; //first die
  $dieRoll[roll()] += 1; //second die
  $dieRoll[roll()] += 1; //third die
  $result[$dieRoll[6]] += 1; //counts the sixes
}
function roll(){
  return random_int(1,6);
}
var_dump($result);

用PHP7 的 random_int 和简单的 rand 函数可能得到如下结果

PHP中的随机性 你觉得自己幸运吗?

如果先看到rand 和 random_int 更好的比较我们可以应用一个公式把结果画在图上。公式是:(php结果-期待的结果)/期待结果的0.5次方。

结果图如下:

PHP中的随机性 你觉得自己幸运吗?

(接近0的值更好)

尽管3个6的结果表现不好,并且这个测试对实际应用来说太过简单我们仍可以看出 random_int 表现优于 rand.

进一步,我们的应用的安全级别由于不可预测性和随机数发生器的可重复行为而得到提升。

PHP5 呢

缺省情况下,PHP5 不提供强壮的随机数发生器。实际上,还是有选择的比如 openssl_random_pseudo_bytes(), mcrypt_create_iv() 或者直接使用fread()函数来使用 /dev/random 或 /dev/urandom 设备。也有一些包比如 RandomLib 或 libsodium.

如果你想要开始使用一个更好的随机数发生器并且同时准备好使用PHP7,你可以使用Paragon Initiative Enterprises random_compat 库。 random_compat 库允许你在 PHP 5.x project.使用 random_bytes() and random_int()

这个库可以通过Composer安装:

composer require paragonie/random_compat
require 'vendor/autoload.php';
$string = random_bytes(32);
var_dump(bin2hex($string));
// string(64) "8757a27ce421b3b9363b7825104f8bc8cf27c4c3036573e5f0d4a91ad2aaec6f"
$int = random_int(0,255);
var_dump($int);
// int(81)

random_compat 库和PHP7使用不同的顺序:

fread() /dev/urandom if available
mcrypt_create_iv($bytes, MCRYPT_CREATE_IV)
COM('CAPICOM.Utilities.1')->GetRandom()
openssl_random_pseudo_bytes()

这个库的一个简单应用用来产生密码:

$passwordChar = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$passwordLength = 8;
$max = strlen($passwordChar) - 1;
$password = '';
for ($i = 0; $i < $passwordLength; ++$i) {
  $password .= $passwordChar[random_int(0, $max)];
}
echo $password;
//possible output: 7rgG8GHu

总结

你总是应该使用一个密码学上安全的伪随机数生成器,random_compat 库提供了一种好的实现。

如果你想要使用可靠的随机数据源,如你在本文所见,建议尽快使用 random_int 和 random_bytes。

以上就是关于php随机性的相关内容,希望对大家的学习有所帮助。

PHP 相关文章推荐
PHP 和 XML: 使用expat函数(一)
Oct 09 PHP
一篇不错的PHP基础学习笔记
Mar 18 PHP
将文件夹压缩成zip文件的php代码
Dec 14 PHP
PHP 存储文本换行实现方法
Jan 05 PHP
模板引擎正则表达式调试小技巧
Jul 20 PHP
第二章 PHP入门基础之php代码写法
Dec 30 PHP
PHP生成自适应大小的缩略图类及使用方法分享
May 06 PHP
php实现短信发送代码
Jul 05 PHP
详谈php静态方法及普通方法的区别
Oct 04 PHP
PHP Mysqli 常用代码集合
Nov 12 PHP
Laravel 的数据库迁移的方法
Jul 31 PHP
三个思路解决laravel上传文件报错:413 Request Entity Too Large问题
Nov 13 PHP
PHP中的session安全吗?
Jan 22 #PHP
PHP下载远程图片并保存到本地方法总结
Jan 22 #PHP
PHP连接MYSQL数据库实例代码
Jan 20 #PHP
CodeIgniter配置之autoload.php自动加载用法分析
Jan 20 #PHP
Twig模板引擎用法入门教程
Jan 20 #PHP
CodeIgniter控制器之业务逻辑实例分析
Jan 20 #PHP
CodeIgniter自定义控制器MY_Controller用法分析
Jan 20 #PHP
You might like
使用apache模块rewrite_module (转)
2007/02/14 PHP
支持中文的php加密解密类代码
2011/11/27 PHP
php求正负数数组中连续元素最大值示例
2014/04/11 PHP
php实例分享之mysql数据备份
2014/05/19 PHP
PHPUnit安装及使用示例
2014/10/29 PHP
详解php中curl返回false的解决办法
2019/03/18 PHP
Javascript 继承机制的实现
2009/08/12 Javascript
jQuery语法高亮插件支持各种程序源代码语法着色加亮
2013/04/27 Javascript
javascript数组快速打乱重排的方法
2014/01/02 Javascript
javascript/jquery获取地址栏url参数的方法
2014/03/05 Javascript
JavaScript获取页面中第一个锚定文本的方法
2015/04/03 Javascript
Javascript实现的Map集合工具类完整实例
2015/07/31 Javascript
通过js修改input、select默认字体颜色
2017/04/19 Javascript
手机注册发送验证码倒计时的简单实例
2017/11/15 Javascript
微信小程序的部署方法步骤
2018/09/04 Javascript
react组件从搭建脚手架到在npm发布的步骤实现
2019/01/09 Javascript
node中IO以及定时器优先级详解
2019/05/10 Javascript
node后端服务保活的实现
2019/11/10 Javascript
使用js原生实现年份轮播选择效果实例
2021/01/12 Javascript
Python 内置函数进制转换的用法(十进制转二进制、八进制、十六进制)
2018/04/30 Python
对python添加模块路径的三种方法总结
2018/10/16 Python
python感知机实现代码
2019/01/18 Python
python3调用windows dos命令的例子
2019/08/14 Python
解决springboot yml配置 logging.level 报错问题
2020/02/21 Python
微信小程序之html5 canvas绘图并保存到系统相册
2019/06/20 HTML / CSS
Android面试宝典
2013/08/06 面试题
项目管理计划书
2014/01/09 职场文书
毕业生大学生活自我总结
2014/01/31 职场文书
《雷雨》教学反思
2014/02/20 职场文书
表彰大会策划方案
2014/05/13 职场文书
小学竞选班干部演讲稿
2014/08/20 职场文书
2015年秋季灭鼠工作总结
2015/07/27 职场文书
2015年国庆放假通知范文
2015/08/18 职场文书
golang中切片copy复制和等号复制的区别介绍
2021/04/27 Golang
Python实现科学占卜 让视频自动打码
2022/04/09 Python
css清除浮动clearfix:after的用法详解(附完整代码)
2023/05/21 HTML / CSS