Note : article en cours de rédation
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.
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 :
base64()
est N, tous les caractères retournés seront réellement aléatoires.=
. Il faut donc se débarrasser des 3 derniers caractères.=
. 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”.
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 :
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.
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 :
Je conserve sous le coude une copie de ce travail (placé sous licence BSD), au cas où. Pour la télécharger, cliquez ici.