Workaround for suhosin.mt_srand.ignore to consiste

2019-06-23 23:09发布

问题:

I have a PHP script which needs to randomise an array with consistent results, so it can present the first few items to the user and they can then pull in more results from the same shuffled set if they want to.

What I'm currently using is this (based on the Fisher Yates algorithm I believe):

function shuffle(&$array, $seed)
{
    mt_srand($seed);
    for ($a=count($array)-1; $a>0; $a--) {
        $b = mt_rand(0, $a);
        $temp = $array[$a];
        $array[$a] = $array[$b];
        $array[$b] = $temp;
    }
}

Which works fine on my local installation, but the server it needs to run on has Suhosin installed, which overrides mt_srand, meaning the seed is ignored, the array is just randomly shuffled and the user gets duplicate results.

Everything I've found on Google suggests I need to disable suhosin.mt_srand.ignore (and suhosin.srand.ignore, not sure if the latter is relevant though) so I put the following in .htaccess:

php_flag suhosin.mt_srand.ignore Off
php_flag suhosin.srand.ignore Off

I have no access to php.ini on this server so AFAIK that's the only way I can do it. The problem is that has no effect - phpinfo() still shows both settings as On, whereas I can change other Suhosin settings using .htaccess no problem.

So I suppose what I'm looking for is either a way to actually disable suhosin.mt_srand.ignore (or a reason why it isn't working), or a workaround to seed a random number generator from within PHP. Or will I just have to implement another RNG myself?

Any help would be much appreciated. Thanks!

回答1:

Using some basic maths and a few tricks you can quite easily create your OWN random function like i have just done :)

Sorry i haven't cleaned it up. It would be much better in a class as you could prevent the need to keep re-seeding it with the previous seed. Don't use a static variable as it limits you to only using 1 seed at a time (or manually keeping track of the seeds yourself). OOP would solve that. Do what you like with the function below, but let me know if you rewrite it.

/**
* returns a decimal between 0 and max_number, requires seeding every time and will ALWAYS return the same decimal for the same seed
* @copyright scott thompson, all rights reserved
* @license MIT (do what you like with this)
* @param string $seed
* @param int $max_number=100 adjust the maximum number range
*/
function random_number($seed, $max_number = 100) {

    //make sure there won't be any deadspace where random numbers will never fill
    if ($max_number > 0xFFFFFF) {
        trigger_error("Max random number was to high. Maximum number of " . 0xFFFFFF . " allowed. Defaulting to maximum number.", E_USER_WARNING);
        $max_number = 0xFFFFFF;
    }

    //hash the seed to ensure enough random(ish) characters each time
    $hash = sha1($seed);

    //use the first x characters, and convert from hex to base 10 (this is where the random number is obtain)
    $rand = base_convert(substr($hash, 0, 6), 16, 10);

    //as a decimal percentage (ensures between 0 and max number)
    return $rand / 0xFFFFFF * $max_number ;

}

$seed = 'hello';
print ($seed = random_number($seed)) . '<br />'; //66.779748605475
print ($seed = random_number($seed)) . '<br />'; //3.5753311857779
print ($seed = random_number($seed)) . '<br />'; //13.994396567011
print ($seed = random_number($seed)) . '<br />'; //70.344917198713
print ($seed = random_number($seed)) . '<br />'; //4.5583250855401

Hope this helps, scott