In-depth understanding of the security of mt_rand () random numbers in PHP, and in-depth understanding of mt_rand
Preface
Many security vulnerabilities related to mt_rand () have been dug up some time ago, which are basically caused by incorrect understanding of random number usage. Here I would like to mention a pitfall of manual on the php official website. Let's take a look at mt_rand (): the Chinese version ^ cn English version ^ en. We can see that the English version has a yellow Caution warning.
This function does not generate cryptographically secure values, and should not be used for cryptographic purposes. If you need a cryptographically secure value, consider using random_int(), random_bytes(), or openssl_random_pseudo_bytes() instead.
Many developers in China are probably reading the Chinese version of the introduction, and using mt_rand () in the program to generate security tokens, core encryption and decryption keys, and so on leads to serious security problems.
Pseudo-Random Number
Mt_rand () is not a real random number generation function. In fact, most random number functions in programming languages generate pseudo random numbers. The difference between a real random number and a pseudo-random number is not explained here. You just need to know a little bit about it.
Pseudo-Random is a pseudo-random number generated by using a seed (common clock) through a deterministic function (commonly used linear coremainder. This means that if you know the seed or the generated random number, you may obtain the information (predictability) of the next random number sequence ).
Let's simply assume that the function for generating random numbers inside mt_rand () is:rand = seed+(i*10)
Here, seed is the random number seed, and I is the number of calls to this random number function. When we know both the I and rand values, we can easily calculate the seed value. For example, if rand = 21 and I = 2 are substituted into the function 21 = seed + (2*10), seed = 1 is obtained. Is it easy? When we get seed, we can calculate the rand value when I is any value.
PHP automatic Seeding
From the previous section, we know that every call to mt_rand () will calculate a pseudo-random number based on the number of seed and the number of calls I. And seed is automatically seeding:
Note: from PHP 4.2.0, srand () or mt_srand () is no longer required for the random number generator, because it is automatically completed by the system.
The problem arises when the system automatically completes seeding. If mt_rand () is automatically seeding each time it is called, then it makes no sense to crack the seed. Manual does not provide detailed information about this. If you have found a circle on the internet, you have to go through the source code ^ mtrand:
PHPAPI void php_mt_srand(uint32_t seed){ /* Seed the generator with a simple uint32 */ php_mt_initialize(seed, BG(state)); php_mt_reload(); /* Seed only once */ BG(mt_rand_is_seeded) = 1; }/* }}} *//* {{{ php_mt_rand */PHPAPI uint32_t php_mt_rand(void){ /* Pull a 32-bit integer from the generator state Every other access function simply transforms the numbers extracted here */ register uint32_t s1; if (UNEXPECTED(!BG(mt_rand_is_seeded))) { php_mt_srand(GENERATE_SEED()); } if (BG(left) == 0) { php_mt_reload(); } --BG(left); s1 = *BG(next)++; s1 ^= (s1 >> 11); s1 ^= (s1 << 7) & 0x9d2c5680U; s1 ^= (s1 << 15) & 0xefc60000U; return ( s1 ^ (s1 >> 18) );}
You can see that each call to mt_rand () will first check whether the seeding has been done. If a random number is directly generated after seeding, php_mt_srand is called for seeding. That is to say, during each php cgi process, only the first call of mt_rand () will be automatically seeding. Next we will generate a random number based on the first seeding seed. In other php operating modes, except CGI (each request starts a cgi process and closes the process after the request ends. Re-read php every time. ini environment variables and so on lead to low efficiency, should not be used much now), basically all is a process after processing the request, standby waits for the next, after processing multiple requests, the requests will be recycled (timeout will also be recycled ).
Write a script to test it.
<?php//pid.phpecho getmypid();
<?php//test.php$old_pid = file_get_contents('http://localhost/pid.php');$i=1;while(true){ $i++; $pid = file_get_contents('http://localhost/pid.php'); if($pid!=$old_pid){ echo $i; break; }}
Test results: (windows + phpstudy)
Apache 1000 request
Nginx 500 request
Of course, this test only confirms the number of requests that can be processed by a process in apache and nginx, and then verifies the conclusion about automatic seeding:
<?php//pid1.phpif(isset($_GET['rand'])){ echo mt_rand();}else{ echo getmypid();}
<?php//pid2.phpecho mt_rand();
<?php//test.php$old_pid = file_get_contents('http://localhost/pid1.php');echo "old_pid:{$old_pid}\r\n";while(true){ $pid = file_get_contents('http://localhost/pid1.php'); if($pid!=$old_pid){ echo "new_pid:{$pid}\r\n"; for($i=0;$i<20;$i++){ $random = mt_rand(1,2); echo file_get_contents("http://localhost/pid".$random.".php?rand=1")." "; } break; }}
The pid is used to determine that when a new process starts, the mt_rand () output of one of the two pages is randomly obtained:
old_pid:972 new_pid:7752 1513334371 2014450250 1319669412 499559587 117728762 1465174656 1671827592 1703046841 464496438 1974338231 46646067 981271768 1070717272 571887250 922467166 606646473 134605134 857256637 1971727275 2104203195
Use the first random number 1513334371 to crack the seeds:
smldhz@vm:~/php_mt_seed-3.2$ ./php_mt_seed 1513334371 Found 0, trying 704643072 - 738197503, speed 28562751 seeds per second seed = 735487048 Found 1, trying 1308622848 - 1342177279, speed 28824291 seeds per second seed = 1337331453 Found 2, trying 3254779904 - 3288334335, speed 28811010 seeds per second seed = 3283082581 Found 3, trying 4261412864 - 4294967295, speed 28677071 seeds per second Found 3
Three possible seeds are cracked, and the quantity is rarely manually tested one by one:
<? Phpmt_srand (735487048); // manually seeding for ($ I = 0; $ I <21; $ I ++) {echo mt_rand ()."";}
Output:
The first 20 digits are exactly the same as those obtained by the above script. Check that the seed is 1513334371. With the seed, we can calculate the random number generated by calling mt_rand. For example, I generated a 21-bit script, and the last one is 1515656265. If you have not accessed the site after running the script, open http: // localhost/pid2.php can see the same 1515656265.
Therefore, we can conclude that:
Php automatic seeding occurs when mt_rand () is called for the first time in the php cgi process. It has nothing to do with the accessed page. As long as the request is processed by the same process, it will share the same automatically seeding seed.
Php_mt_seed
We already know that the generation of random numbers depends on specific functions.rand = seed+(i*10)
. For such a simple function, of course, we can directly calculate (calculate) A (Group) solution, but the actual use of mt_rand () function is quite complex and cannot be reversed. An effective method is to enumerate all the seeds and compare them based on the random number sequence generated by the seed with the known random number sequence to verify whether the seed is correct. Php_mt_seed ^ phpmtseed is such a tool. It is very fast and it takes several minutes to finish running 2 ^ 32-bit seed. It can directly crack the possible seed based on the output result of a single mt_rand () (the example above). Of course, it can also crack similarmt_rand(1,100)
This limits the seeds output by min max (used in the following instances ).
Security Questions
So many times, why is the random number insecure? In fact, there is no problem with the function itself, and the official news clearly shows that the generated random number should not be used for security encryption (although the Chinese version of manual is not written ). The problem is that developers are not aware that this is not a real random number. We already know that seeds can be cracked through known random number sequences. That is to say, as long as a random number exists in any page or its derivative value (reversible push random value), the random number of other arbitrary pages will no longer be "random number ". Common examples of random numbers output, such as verification codes and random file names. Common random numbers are used for security verification, such as retrieving the password verification value, such as the encryption key. An ideal attack scenario:
In the dark of the night, wait for apache (nginx) to reclaim all php processes (ensure that the next visit will be re-planted), visit the verification code page once, according to the Verification Code character inverse random number, then according to the random number to crack the random number of seeds. Next, visit the password retrieval page. The generated password retrieval link is based on a random number. We can easily calculate this link and retrieve the Administrator's password ............ XXOO
Instance
PHPCMS MT_RAND seed crack Causes authkey leakage. Yu Niu writes better than me. It's enough to see him.
Discuz x3.2 authkey leakage is similar. Patches have been released on the official website. If you are interested, you can analyze the patches by yourself.
Summary
The above is all the content of this article. I hope the content of this article has some reference and learning value for everyone's learning or work. If you have any questions, please leave a message to us, thank you for your support.