web-dev-qa-db-fra.com

Génération de nombres aléatoires sécurisée dans PHP

Cas d'utilisation: le bouton "J'ai oublié mon mot de passe". Nous ne pouvons pas trouver le mot de passe d'origine de l'utilisateur car il est stocké sous forme hachée, donc la seule chose à faire est de générer un nouveau mot de passe aléatoire et de le lui envoyer par e-mail. Cela nécessite des nombres aléatoires cryptographiquement imprévisibles, pour lesquels mt_Rand n'est pas assez bon, et en général, nous ne pouvons pas supposer qu'un service d'hébergement fournira un accès au système d'exploitation pour installer un module de nombres aléatoires cryptographiques, etc. donc je cherche un moyen pour générer des nombres aléatoires sécurisés dans PHP lui-même.

La solution que j'ai trouvée jusqu'à présent consiste à stocker une graine initiale, puis pour chaque appel,

result = seed
seed = sha512(seed . mt_Rand())

Ceci est basé sur la sécurité de la fonction de hachage sha512 (l'appel mt_Rand est juste pour rendre la vie un peu plus difficile à un adversaire qui obtient une copie de la base de données).

Est-ce que je manque quelque chose ou existe-t-il des solutions mieux connues?

34
rwallace

Vous pouvez également envisager d'utiliser OpenSSL openssl_random_pseudo_bytes, il est disponible depuis PHP 5.3.

 string openssl_random_pseudo_bytes ( int $length [, bool &$crypto_strong ] )

Génère une chaîne d'octets pseudo-aléatoires, avec le nombre d'octets déterminé par le paramètre de longueur. Il indique également si un algorithme cryptographiquement puissant a été utilisé pour produire les octets pseudo-aléatoires, et ce via le paramètre optionnel crypto_strong. Il est rare que cela soit FAUX, mais certains systèmes peuvent être défectueux ou vieux.

http://www.php.net/manual/en/function.openssl-random-pseudo-bytes.php

Puisque PHP 7, il y a aussi random_bytes fonction disponible

string random_bytes ( int $length )

http://php.net/manual/en/function.random-bytes.php

40
Eugene Dounar

Je recommande fortement de cibler/dev/urandom sur les systèmes unix ou la crypto-api sur la plate-forme Windows comme source d'entropie pour les mots de passe.

Je ne saurais trop insister sur l'importance de réaliser que les hachages ne sont PAS des dispositifs magiques augmentant l'entropie. Les abuser de cette manière n'est pas plus sûr que d'utiliser les données de départ et de Rand () avant qu'elles ne soient hachées et je suis sûr que vous reconnaissez que ce n'est pas une bonne idée. La graine annule (déterministe mt_Rand ()) et il n'y a donc aucun intérêt à l'inclure.

Les gens pensent être intelligents et intelligents et le résultat de leur travail sont des systèmes et des appareils fragiles qui mettent la sécurité de leurs systèmes et la sécurité des autres systèmes (via de mauvais conseils) en péril inutile.

Deux torts ne font pas un droit. Un système est aussi fort que sa partie la plus faible. Ce n'est pas une licence ou une excuse pour accepter de rendre encore plus incertain.


Voici quelques PHP code pour obtenir une chaîne aléatoire sécurisée de 128 bits, de ce commentaire sur php.net par Mark Seecof :

"Si vous avez besoin de bits pseudo-aléatoires pour des raisons de sécurité ou de chiffrement (oeuf, IV aléatoire pour le chiffrement par bloc, sel aléatoire pour le hachage de mot de passe), mt_Rand () est une mauvaise source. Sur la plupart des plates-formes Unix/Linux et/ou MS-Windows, vous pouvez obtenir une meilleure qualité de bits pseudo-aléatoires du système d'exploitation ou de la bibliothèque système, comme ceci:

<?php
// get 128 pseudorandom bits in a string of 16 bytes

$pr_bits = '';

// Unix/Linux platform?
$fp = @fopen('/dev/urandom','rb');
if ($fp !== FALSE) {
    $pr_bits .= @fread($fp,16);
    @fclose($fp);
}

// MS-Windows platform?
if (@class_exists('COM')) {
    // http://msdn.Microsoft.com/en-us/library/aa388176(VS.85).aspx
    try {
        $CAPI_Util = new COM('CAPICOM.Utilities.1');
        $pr_bits .= $CAPI_Util->GetRandom(16,0);

        // if we ask for binary data PHP munges it, so we
        // request base64 return value.  We squeeze out the
        // redundancy and useless ==CRLF by hashing...
        if ($pr_bits) { $pr_bits = md5($pr_bits,TRUE); }
    } catch (Exception $ex) {
        // echo 'Exception: ' . $ex->getMessage();
    }
}

if (strlen($pr_bits) < 16) {
    // do something to warn system owner that
    // pseudorandom generator is missing
}
?>

NB: il est généralement prudent de laisser à la fois la tentative de lecture/dev/urandom et la tentative d'accès à CAPICOM dans votre code, bien que chacune échouera silencieusement sur la plate-forme de l'autre. Laissez-les tous les deux ici pour que votre code soit plus portable. "

59
Einstein

PHP est livré avec un nouvel ensemble de fonctions CSPRNG (random_bytes() et random_int()). Il est trivial de transformer cette dernière fonction en une fonction de générateur de chaîne:

<?php
/**
 * Generate a random string, using a cryptographically secure 
 * pseudorandom number generator (random_int)
 * 
 * For PHP 7, random_int is a PHP core function
 * For PHP 5.x, depends on https://github.com/paragonie/random_compat
 * 
 * @param int $length      How many characters do we want?
 * @param string $keyspace A string of all possible characters
 *                         to select from
 * @return string
 */
function random_str(
    $length,
    $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
) {
    $str = '';
    $max = mb_strlen($keyspace, '8bit') - 1;
    if ($max < 1) {
        throw new Exception('$keyspace must be at least two characters long');
    }
    for ($i = 0; $i < $length; ++$i) {
        $str .= $keyspace[random_int(0, $max)];
    }
    return $str;
}

Si vous avez besoin de l'utiliser dans un projet PHP 5), n'hésitez pas à récupérer une copie de random_compat , qui est un polyfill pour ces fonctions.

7
Scott Arciszewski