PHP中的随机数安全问题

Posted 合天智汇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PHP中的随机数安全问题相关的知识,希望对你有一定的参考价值。

引言

西湖论剑杯线上预选赛线上赌场一题,明文攻击出来的hint中给了/flag/seed.txt以及一个字符串code,这里需要稍微脑洞一点想到seed是指随机数种子,以及Web页面上的code值是每小时更换的"随机数":

PHP中的随机数安全问题


我们利用phpmtseed工具(C编写,速度很快)可以根据随机数碰撞出随机数的种子,从而获取flag:

PHP中的随机数安全问题

以下是原理分析:

PHP中的随机数安全问题

随机数的安全缺陷

随机数广泛应用于生成验证码、Token、密钥等场景中,分为真随机数和伪随机数。我们通过算法(常用线性同余)和种子(常用时钟)得到的随机数属于伪随机数:当知道种子或已产生的随机数时,随机数序列是可以被预测的。

PHP中的随机数安全问题

可以看到PHP Manual其实提示了生成随机数用于加密是不安全的,但是这个Caution不知为何只存在于英文版的PHP Manual中,中文版被遗漏了...这可能也是很多国内的开发应用出现过此缺陷的一个原因。

PHP中生成随机数的函数有rand()mtrand(),它们分别对应srand()mtstrand()两个用于播种随机数种子的函数。我们建立rand.php进行测试:

  
    
    
  
  1. <?php

  2. mt_srand(2333);

  3. srand(2333);

  4. echo "seed=2333,rand()产生的随机数序列: ";

  5. for($i=1;$i<=3;$i++){

  6.   echo rand()." ";

  7. }

  8. echo "seed=2333,mt_rand()产生的随机数序列: ";

  9. for($i=1;$i<=3;$i++){

  10.   echo mt_rand()." ";

  11. }

  12. ?>

执行:

PHP中的随机数安全问题

可以看出当随机数种子相同时,不管是rand()还是mtrand()产生的随机数序列都是相同的,如果seed泄露则会导致随机数序列的泄露。当种子值为固定如mtsrand(1000)时,随机数形同虚设;而使用动态种子也未必安全,如:

  
    
    
  
  1. //seed值较小,直接遍历爆破

  2. mt_srand(mt_rand(0,1000));

  3. //用公开的time()作为种子,和静态种子一样危险

  4. mt_srand(time());

  5. //破解时要注意服务器时间可能存在偏差,需要设定一个较小的范围

自PHP 4.2.0 起,随机数发生器会自动完成播种,不再需要手工调用srand()或mt_srand(),但是这样仍旧不安全,我们分别对两个函数进行讨论


PHP中的随机数安全问题

rand()

rand()在产生随机数时不会自动调用srand(),产生的随机数序列可以通过这个式子预测:

state[i] = state[i-3] + state[i-31]

所以我们可以收集rand()生成的32位以上的随机序列,以预测后面的随机序列。

详细参考:Cracking-Php-Rand

(http://www.sjoerdlangkemper.nl/2016/02/11/cracking-php-rand/)

并且在某些平台下rand()最大值为32767,非常容易遭到爆破。


PHP中的随机数安全问题

mt_rand()

根据PHP Manual,mtrand()产生随机数值的平均速度比libc提供的rand()快四倍,rand() 函数默认使用 libc 随机数发生器,mtrand() 函数是非正式用来替换它的。

mtrand()函数的安全缺陷主要出现在,所谓"自动播种"其实是PHP在同一个请求进程中只会进行一次播种,也就是说即使多次调用mtrand()函数,也只会根据第一次播种的种子生成随机数。这一结论的证明可以通过mt_rand()的源码分析或写个小脚本测试来完成,不再展开,函数的核心实现代码是这一部分:

  
    
    
  
  1. PHPAPI void php_mt_srand(uint32_t seed)

  2. {

  3.    /* Seed the generator with a simple uint32 */

  4.    php_mt_initialize(seed, BG(state));

  5.    php_mt_reload();

  6.    /* Seed only once */

  7.    BG(mt_rand_is_seeded) = 1;

  8. }

  9. /* }}} */

  10. /* {{{ php_mt_rand

  11. */

  12. PHPAPI uint32_t php_mt_rand(void)

  13. {

  14.    /* Pull a 32-bit integer from the generator state

  15.       Every other access function simply transforms the numbers extracted here */

  16.    register uint32_t s1;

  17.     if (UNEXPECTED(!BG(mt_rand_is_seeded))) {

  18.        php_mt_srand(GENERATE_SEED());

  19.    }

  20.    if (BG(left) == 0) {

  21.        php_mt_reload();

  22.    }

  23.    --BG(left);

  24.    s1 = *BG(next)++;

  25.    s1 ^= (s1 >> 11);

  26.    s1 ^= (s1 <<  7) & 0x9d2c5680U;

  27.    s1 ^= (s1 << 15) & 0xefc60000U;

  28.    return ( s1 ^ (s1 >> 18) );

  29. }

由于根据种子生成随机数序列的计算并不可逆,有效的破解方法应该是穷举种子并生成随机数序列,与已知的随机数(序列)作比较,这也是文章开头提到的phpmtseed工具的实现逻辑。

PHP中的随机数安全问题

安全建议

涉及到加密/权限/CSRF Token等敏感操作时:

  • 不要使用时间函数作为种子或直接作为随机数:time()/microtime()

  • 不要直接使用rand()``mt_rand()这样的弱伪随机数生成器

  • 随机数要足够长以防御暴力破解


PHP中的随机数安全问题

相关题目学习

0CTF 2016 Rand2:http://www.vuln.cn/6004

NJCTF 2017 Guess:

https://www.cnblogs.com/afanti/p/8722760.html


PHP中的随机数安全问题

相关操作训练

PHP常见危险函数:了解PHP常见的危险函数,使用这些函数可能带来的程序脆弱性;点击文末“阅读原文”,可立即操作!

PHP中的随机数安全问题



参考链接:http://wonderkun.cc/index.html/?p=585




PHP中的随机数安全问题

别忘了投稿哦

大家有好的技术原创文章

欢迎投稿至邮箱:edu@heetian.com 

合天会根据文章的时效、新颖、文笔、实用等多方面评判给予100元-500元不等的稿费哦

有才能的你快来投稿吧!

了解投稿详情点击


“阅读原文”,开始操作学习!

以上是关于PHP中的随机数安全问题的主要内容,如果未能解决你的问题,请参考以下文章

PHP中的随机数安全问题

PHP 中的安全随机数生成

php 随机WordPress片段

PHP中的几个随机数生成函数

播放随机声音而不重复

使用 PHP 生成随机十六进制颜色代码