本文主要分析以加密为目的的随机数生成问题。PHP 5 并未提供生成强加密随机数的简便机制,但是,PHP 7 引入了两个 CSPRNG 函数以解决该问题。系 OneAPM 工程师编译整理。

什么是 CSPRNG?

引用维基百科的定义,密码安全的虚拟随机数生成器(Cryptographically Secure Pseudorandom Number Generator,CSPRNG)是带有特定属性使之在密码学中适用的虚拟随机数生成器(pseudo-random number generator,PRNG)。

CSPRNG 主要用于:

  • 生成键(比如:生成复杂的键)
  • 为新的用户账号生成随机密码
  • 加密系统

保证高安全水准的一个重要因素便是高质量的随机数。

PHP 7 中的 CSPRNG

PHP 7 为 CSPRNG 引入了两种新函数:random_bytesrandom_int

random_bytes 函数返回 string 类型,并接受一个 int 类型为参数,该参数规定了所返回字符串的字节长度。

例如:

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

random_int 函数返回给定范围内的整型数字。

举例:

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

幕后解密

以上函数的随机数来源因环境不同而有所差异:

  • 在 Windows 系统,会使用 CryptGenRandom() 函数。
  • 在其他平台,会优先使用 arc4random_buf() 函数(限 BSD 衍生系统或带 libbsd 的系统)。
  • 若以上两点均不符合,会使用 Linux [getrandom(2)](http://man7.org/linux/man-pages/man2/ getrandom.2.html) 系统调用。
  • 若以上来源均不符合,会抛出 Error

一个简例

一个好的随机数生成系统能确保生成质量适合的随机数。为了检验质量,需要运行一系列的统计试验。此处,暂不深入讨论复杂的统计话题,将已知的行为与随机数生成器的结果进行比较,有助于质量评估。

一个简单的测试方法是掷骰游戏。假设投掷一次,投出6的概率是1/6。如果同时投掷三个骰子,投100次,投得零次、一次、两次及三次6的次数大概是:

  • 0 次6 = 57.9 次
  • 1 次6 = 34.7 次
  • 2 次6 = 6.9 次
  • 3 次6 = 0.5 次

以下是骰子投掷100万次的代码:

$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);

用 PHP 7 的 random_int 与简单的 rand 函数测试上面的代码,可能会得到:

Sixes expected random_int rand
0 579000 579430 578179
1 347000 346927 347620
2 69000 68985 69586
3 5000 4658 4615

更直观地查看 randrandom_int 的差别,可以运用方程式放大两组结果的差异,并绘制成图表:

php result - expected result / sqrt(expected)

得到的结果如下:



(结果越接近零越好)

即便三个6的组合表现一般,且该测试与真实应用相比太过简单,我们也能清楚地看到 random_int 的表现优于 rand。况且,随机数生成器的可预见行为、重复行为越少,应用的安全程度就更高。

PHP 5 又如何呢?

默认情况下,PHP 5 并未提供任何强虚拟随机数生成器。而实际使用中,可以使用 openssl_random_pseudo_bytes()mcrypt_create_iv() 方法,或直接结合使用 /dev/random/dev/urandomfread() 方法。此外,还有包 RandomLiblibsodium

如果你想用一个比较好的随机数生成器,同时能与 PHP 7 兼容,你可以使用 Paragon Initiative 公司的 random_compat 库。该库允许在 PHP 5.x 项目中使用 random_bytes()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 库使用了与 PHP 7 中不同的优先序列:

  1. 如果可用,先使用 fread() /dev/urandom
  2. mcrypt_create_iv($bytes, MCRYPT_CREATE_IV)
  3. COM('CAPICOM.Utilities.1')->GetRandom()
  4. 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_intrandom_bytes 吧!

原文地址:http://www.sitepoint.com/randomness-php-feel-lucky/

OneAPM for PHP 能够深入到所有 PHP 应用内部完成应用性能管理 能够深入到所有 PHP 应用内部完成应用性能管理和监控,包括代码级别性能问题的可见性、性能瓶颈的快速识别与追溯、真实用户体验监控、服务器监控和端到端的应用性能管理。想阅读更多技术文章,请访问 OneAPM 官方技术博客

PHP 中的随机数——你觉得可靠么?的更多相关文章

  1. 为什么说Java中的随机数都是伪随机数?

    什么是伪随机数?  1.伪随机数是看似随机实质是固定的周期性序列,也就是有规则的随机. 2.只要这个随机数是由确定算法生成的,那就是伪随机,只能通过不断算法优化,使你的随机数更接近随机.   (随机这 ...

  2. Linux中的随机数文件 /dev/random /dev/urandom

    Linux中的随机数可以从两个特殊的文件中产生,一个是/dev/urandom.另外一个是/dev/random.他们产生随机数的原理是利用当前系统的熵池来计算出固定一定数量的随机比特,然后将这些比特 ...

  3. Java中的随机数生成器:Random,ThreadLocalRandom,SecureRandom

    Java中的随机数生成器:Random,ThreadLocalRandom,SecureRandom 文中的 Random即:java.util.Random,ThreadLocalRandom 即: ...

  4. numpy中的随机数模块

    https://www.cnblogs.com/td15980891505/p/6198036.html numpy.random模块中提供啦大量的随机数相关的函数. 1 numpy中产生随机数的方法 ...

  5. Java课程课后作业190315之从文档中读取随机数并得到最大连续子数组

    从我上一篇随笔中,我们可以得到最大连续子数组. 按照要求,我们需要从TXT文档中读取随机数,那在此之前,我们需要在程序中写入随机数 import java.io.File; import java.i ...

  6. Java中产生随机数的两个方法

    Java中产生随机数的两个方法 一.利用random方法来生成Java随机数. 在Java语言中生成Java随机数相对来说比较简单,因为有一个现成的方法可以使用.在Math类中,Java语言提供了一个 ...

  7. 关于LUA中的随机数问题

    也许很多人会奇怪为什么使用LUA的时候,第一个随机数总是固定,而且常常是最小的那个值,下面我就简要的说明一下吧,说得不好,还请谅解.我现在使用的4.0版本的LUA,看的代码是5.0的,呵呵 LUA4. ...

  8. C语言中生产随机数 rand()函数

    参考资料:C语言中产生随机数 一:如果你只要产生随机数而不需要设定范围的话,你只要用rand()就可以了:rand()会返回一随机数值, 范围在0至RAND_MAX 间.RAND_MAX定义在stdl ...

  9. LoadRunner中的随机数

    LoadRunner中的随机数 Action() { int i; ]; srand(time(NULL)); i=rand()%; lr_save_datetime("%m%d%H%M%S ...

随机推荐

  1. Learning Django Resources

    Learning Django Django makes it easier to build better Web apps more quickly and with less code. Web ...

  2. sqlserver 连不上的问题

    tcp ip  属性 ipall  属性改为1433

  3. mysql自增

    主键设置自增,同时主键需要是int类型

  4. Extjs combobox设置默认值

    转载:http://www.54mask.com/extjs-combobox-default-value.html 相信很多人都遇到了在ExtJS框架中设置combo组件默认值的需求,ExtJS框架 ...

  5. 16Aspx.com源码2014年7月详细

            Web电子商务网(三层)V2.0源码 2014-07-31   [VS2010] 源码介绍: Web电子商务网(三层)V2.0源码 源码描述: 一.源码特点     采用三层架构开发, ...

  6. Log4Net详细配置

    关于Log4Net配置主要分几步 第一步:下载log4net.dll(log4net官网:http://logging.apache.org/log4net/download_log4net.cgi) ...

  7. 实现View弹性滑动例子

    弹性滑动原理 将一次大的滑动非为若干次小的滑动,并在一个时间段内完成.有一种顺畅的感觉更-好的用户体验 实现方式很多种,包括用Scroller.动画.延时策略. 使用Handler实现弹性滑动 效果可 ...

  8. ACM——进制转换

    http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1012 进制转换 时间限制(普通/Jav ...

  9. spring mvc 笔记

    springmvc 课堂笔记 1.Springmvc是什么 Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想, ...

  10. Linux的前世今生

    Linux的起源 说到Linux[/ˈlɪnəks/],想必大家也会自然而然地想到他的创始人——被称为“Linux之父”的林纳斯·托瓦兹(Linus Torvalds).其实,在Linux出现之前,还 ...