PHP mt_rand应用场景详解

时间:2019-04-19
本文章向大家介绍PHP mt_rand应用场景详解,主要包括PHP mt_rand应用场景详解使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

php_mt_seed应用场景
直接使用mt_rand生成的随机数
假设下面的代码为用户密码的随机生成代码:

<?php

 function user_password() {

 return mt_rand();

 }

 

 echo user_password(), "\n";

 echo user_password(), "\n";

 echo user_password(), "\n";

运行后我们可以得到三个用户的密码

假设我们现在得到了第一个用户的密码:1412203388

通过这个密码我们可以猜测出后面两个用户的密码。

下面我们运行php_mt_seed找出seed,命令如下:

./php_mt_seed.exe 1412203388

这里我用于测试的服务器的PHP版本为5.4.45,那么seed就可能是2078089285,下面写一段PHP代码来测试一下。

<?php

mt_srand(2078089285);//手工播种

for($i=0;$i<3;$i++){

 echo mt_rand()." ";

}

完全解密了其它两个用户的密码。

使用经过转换后的mt_rand随机序列
下面是我们更常见到的生成随机数的代码:

<?php

 function user_password($length = 10) {

 $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';

 $len = strlen($allowable_characters) - 1;

 $pass = '';

 for ($i = 0; $i < $length; $i++) {

 $pass .= $allowable_characters[mt_rand(0, $len)];

 }

 return $pass;

 }

 mt_srand(time());

 echo user_password(), "\n";

 echo user_password(), "\n";

 echo user_password(), "\n";

 ?>

运行后我们可以得到三个用户的密码:

假设我们现在得到了第一个用户的密码:paJHuuKv3H

我们要写一个程序,先是把字母还原成为生成的随机数,然后在拼接成php_mt_seed需要的参数。代码如下:

<?php

 $pass_now = "paJHuuKv3H";

 $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';

 $len = strlen($allowable_characters) - 1;

 for($j = 0; $j < strlen($pass_now); $j++)

 {

 for ($i = 0; $i < $len; $i++) {

 if($pass_now[$j] == $allowable_characters[$i])

 {

 echo "$i $i 0 56 ";

 break;

 }

 }

 }

执行如下命令:

./php_mt_seed.exe 14 14 0 56 0 0 0 56 33 33 0 56 32 32 0 56 19 19 0 56 19 19 0 56 34 34 0 56 20 20 0 56 50 50 0 56 32 32 0 56
运行结果如下:

得到seed为1544796235,写解密代码如下:

<?php

 function user_password($length = 10) {

 $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';

 $len = strlen($allowable_characters) - 1;

 $pass = '';

 for ($i = 0; $i < $length; $i++) {

 $pass .= $allowable_characters[mt_rand(0, $len)];

 }

 return $pass;

 }

 

 mt_srand(1544796235);

 

 echo user_password(), "\n";

 echo user_password(), "\n";

 echo user_password(), "\n";

完全解密了其它两个用户的密码。

下面列出一个实际的例子:

Discuz X3.3 authkey生成算法的安全性漏洞
这里只做简要介绍,如需查看详细内容请看参考文献3。

Discuz官方于2017年8月1号发布最新版X3.4版本,在最新版本中修复了多个安全问题。

用户在初次安装软件时,系统会自动生成一个authkey写入全局配置文件和数据库,之后安装文件会被删除。该authkey用于对普通用户的cookie进行加密等密码学操作,但是由于生成算法过于简单,可以利用公开信息进行本地爆破。

Discuz_X3.3_SC_UTF8uploadinstallindex.php中authkey的生成方法如下:

authkey=substr(md5(authkey = substr(md5(_SERVER[‘SERVER_ADDR’].SERVER[HTTPUSERAGENT]._SERVER[&#x27;HTTP_USER_AGENT&#x27;].dbhost.dbuser.dbuser.dbpw.dbname.dbname.username.password.password.pconnect.substr($timestamp, 0, 6)), 8, 6).random(10);
可以看出authkey主要由两部分组成:

MD5的一部分(前6位) + random生成的10位

跟入random函数:

由于字符生成集合是固定的,且没有重复字符,那么函数中每一次生成hash都唯一对应了chars数组中的一个位置,而且是使用同一个seed生成的。

在之后的代码中使用了同样的random函数:

$config[‘cookie’][‘cookiepre’] = random(4).’’;
Cookie的前四个字节是已知的,并且使用了同样的random函数,那么思路很明显:

通过已知的4位,算出random使用的种子,进而得到authkey后10位。那剩下的就需要搞定前6位,根据其生成算法,只好选择爆破的方式,由于数量太大,就一定要选择一个本地爆破的方式(即使用到authkey而且加密后的结果是已知的)。

在调用authcode函数很多的地方都可以进行校验,在这里使用找回密码链接中的id和sign参数。

sign生成的方法如下:

function dsign($str, $length = 16){

return substr(md5(str.getglobal(config/security/authkey)),0,(str.getglobal(&#x27;config/security/authkey&#x27;)), 0, (length ? max(8, $length) : 16));

}

爆破authkey 的流程:

1.通过cookie前缀爆破随机数的seed。使用php_mt_seed工具。

2.用seed生成random(10),得到所有可能的authkey后缀。

3.给自己的账号发送一封找回密码邮件,取出找回密码链接。

4.用生成的后缀爆破前6位,范围是0×000000-0xffffff,和找回密码url拼接后做MD5求出sign。

5.将求出的sign和找回密码链接中的sign对比,相等即停止,获取当前的authkey。

总结
说了这么多,那到底随机数怎么不安全了呢?其实函数本身没有问题,官方也明确提示了生成的随机数不应用于安全加密用途(虽然中文版本manual没写)。问题在于开发者并没有意识到这并不是一个真随机数 。我们已经知道,通过已知的随机数序列可以爆破出种子。也就是说,只要任意页面中存在输出随机数或者其衍生值(可逆推随机值),那么其他任意页面的随机数将不再是“随机数”。常见的输出随机数的例子比如验证码,随机文件名等等。常见的随机数用于安全验证的比如找回密码校验值,比如加密key等等。PHP随机数的应用范围很广,很多知名的程序也出现过很多严重的问题,但是,目前这个方面还是没有得到足够的重视,一定还有很多很多地方存在着类似的漏洞等待着我们去发现。


http://www.45zq.cn/portal/article/index/id/38.html