Outils pour utilisateurs

Outils du site


web:php:random

L'aléatoires en PHP

Note : article en cours de rédation


Crypto

L'utilisation de fonctions telles que rand() ou mt_rand() n'est pas considéré comme sécurisé pour une utilisation en cryptographie. S'il existe des fonctions préférables, elles sont généralement inclues dans des modules non installés par défaut.

Méthode générale (linux)

Sous Linux/Unix, une solution consiste à aller lire le fichier /dev/random, qui génère des nombres aléatoires. On peut indiquer le nombre d'octets qu'on souhaite lire :

$nbBytes = 1;
$random = file_get_contents('/dev/random', false, null, 0, $nbBytes);

Cet exemple retournera 1 octet aléatoires. Pour rappel, 1 octet = 8 bits.

Ces caractères seront illisibles. Si l'on souhaite les convertir en chaine, le plus simple peut être de les convertir en base64 (caractères de 6 bits : 26 = 64).

$base64 = base64_encode($random);
echo $base64;

L'exemple ci-dessus affichera par exemple Mg==.

On a en entrée un multiple de 8 bits, et en sortie un multiple de 6. Leur plus petit commun multiple (PPCM) est 24 (24 bits = 3 octets ou 4 caractères en base64).

Du coup, pour chaque groupon de 3 octets en entrée, on se retrouvera avec 4 caractères en sortie. Mais si on ne fourni pas un multiple de 3, comme dans notre exemple, on obtiendra tout de même 24 bits en sortie.

Ainsi, avec un octet :

//octet en entrée : 8 bits, où X peut valoir 0 ou 1
XXXXXXXX

//on complète pour avoir un multiple de 6 bits
XXXXXXXX0000

//on découpe en caractères de 6 bits
XXXXXXXX0000
\____/\____/
  M     g

//on complète pour retourner 4 caractères
XXXXXXXX0000++++++++++++
\____/\____/\____/\____/
  M     g     =     =

Si j'ai distingué ci-dessus le 0 du +, c'est parce que = qu'on a en sorti n'existe par réellement dans la table des caractères en base64.

De fait, dans notre exemple, le second caractère ne pourra avoir que 4 valeurs différentes : A (000000), Q (010000), g (100000) ou w (110000). Il n'est donc pas aussi aléatoire que le premier.

En résumé, où N est un multiple de 3 :

  • si le nombre d'octets fournis en entrée de base64() est N, tous les caractères retournés seront réellement aléatoires.
  • Si il est N+1, il y aura 1 caractère un peu moins aléatoire suivi de 2 =. Il faut donc se débarrasser des 3 derniers caractères.
  • Si il est N+2, il y aura 1 caractère un peu moins aléatoire suivi de 1 =. Il faut donc se débarrasser des 2 derniers caractères.

Pour calculer le nombre de caractère qu'on va obtenir, ou le nombre de caractère utiles (pour ne garder que l'essentiel), on peut faire :

function base64CharForByteString($length = 1, $usefulCharOnly = false;) {
    if($usefulCharOnly === true) {
        //number of really random characters returned by base64() for $nbBytes bytes
        return (int)($length*8/6);
    } else {
        //number of characters returned by base64() for $nbBytes bytes
        return ((int)(($length+2)/3)) * 4;
    }
}

A l'inverse si on sait le nombre de caractères que l'on souhaite, voici une méthode pour calculer le nombre d'octets qu'il nous faudra en entrée :

function bytesNeededForBase64String($length) {
    return ceil($length * 6 / 8);
}

Après, il ne reste plus qu'à tronquer notre chaine pour garder la partie “utile”.

random ou urandom ?

Sur certains systèmes, on peut utiliser au choix le fichier /dev/random ou le fichier /dev/urandom.

En réalité, si les deux font presque la même chose, urandom utilise une entropie plus faible :

  • inconvéniant : un peu moins sécurisé d'un point de vue cryptographique, car résultat plus déterministe
  • avantage : est capable de générer une plus grande quantité d'octets dans une même période.

En bref : pour utiliser dans des sels de hashage, il vaut mieux se tourner vers /dev/random, et n'utiliser /dev/urandom que dans des cas qui nécessitent de grosses quantités de données aléatoires, où /dev/random ne suffit plus.

Et Windows ?

Il n'existe pas d'équivalent direct à /dev/random sous Windows. Une solution consiste à utiliser la librairie de George Argyros : Secure Random Bytes in PHP.

La librairie en question tente de trouver la manière la plus sécurisée disponible sur votre système. Dans l'ordre il tente :

  • openssl_random_pseudo_bytes
  • mcrypt_create_iv
  • /dev/urandom
  • Méthode custom si aucune autre n'est disponible (celui qui sera probablement utilisé sur Windows).

Je conserve sous le coude une copie de ce travail (placé sous licence BSD), au cas où. Pour la télécharger, cliquez ici.

web/php/random.txt · Dernière modification : le 27/02/2013 à 12:37 de Yosko