web-dev-qa-db-fra.com

Quel est le meilleur moyen de générer une clé aléatoire dans PHP?

Je cherche à créer une fonction réutilisable qui générera une clé aléatoire avec des caractères ACSII imprimables de la longueur choisie (de 2 à 1 000+). Je pense que les caractères imprimables ASCII seraient 33-126. Il n'est pas nécessaire que leur clé soit complètement unique, elle est unique si elle est générée exactement à la même milliseconde (donc uniqid() ne fonctionnera pas).

Je pense qu'une combinaison de chr() et mt_Rand() pourrait fonctionner.

Est-ce la voie à suivre ou une autre méthode est-elle la meilleure?

Edit: uniqid() ne fonctionnera pas non plus car il n’a pas de paramètre de longueur, c’est ce que PHP vous donne.

My Idea : C'est ce que je suis venu avec:

function GenerateKey($length = 16) {
    $key = '';

    for($i = 0; $i < $length; $i ++) {
        $key .= chr(mt_Rand(33, 126));
    }

    return $key;
}

Y at-il des problèmes avec cela?

Another Edit: La plupart des autres questions traitent de la génération de mot de passe. Je veux une plus grande variété de caractères et je me fiche de 1 vs l. Je veux que le nombre maximum de clés possibles soit possible.

Remarque: la clé générée ne doit pas nécessairement être sécurisée par cryptographie.

37
Darryl Hein

Update (12/2015): Pour PHP 7.0, vous devez utiliser random_int() au lieu de mt_Rand car il fournit "des valeurs cryptographiquement sécurisées"

Personnellement, j'aime utiliser sha1(microtime(true).mt_Rand(10000,90000)) mais vous recherchez une approche plus personnalisable, essayez cette fonction (qui modifie votre demande de cette réponse ):

function Rand_char($length) {
  $random = '';
  for ($i = 0; $i < $length; $i++) {
    $random .= chr(mt_Rand(33, 126));
  }
  return $random;
}

Néanmoins, cela sera probablement beaucoup plus lent que uniqid (), md5 () ou sha1 ().

Edit: On dirait que tu y es arrivé en premier, désolé. :RÉ

Edit 2: J'ai décidé de faire un petit test sympa sur ma machine Debian avec PHP 5 et eAccelerator (excusez le code long):

function Rand_char($length) {
  $random = '';
  for ($i = 0; $i < $length; $i++) {
    $random .= chr(mt_Rand(33, 126));
  }
  return $random;
}

function Rand_sha1($length) {
  $max = ceil($length / 40);
  $random = '';
  for ($i = 0; $i < $max; $i ++) {
    $random .= sha1(microtime(true).mt_Rand(10000,90000));
  }
  return substr($random, 0, $length);
}

function Rand_md5($length) {
  $max = ceil($length / 32);
  $random = '';
  for ($i = 0; $i < $max; $i ++) {
    $random .= md5(microtime(true).mt_Rand(10000,90000));
  }
  return substr($random, 0, $length);
}

$a = microtime(true);
for ($x = 0; $x < 1000; $x++)
  $temp = Rand_char(1000);

echo "Rand:\t".(microtime(true) - $a)."\n";

$a = microtime(true);
for ($x = 0; $x < 1000; $x++)
  $temp = Rand_sha1(1000);

echo "SHA-1:\t".(microtime(true) - $a)."\n";

$a = microtime(true);
for ($x = 0; $x < 1000; $x++)
  $temp = Rand_md5(1000);

echo "MD5:\t".(microtime(true) - $a)."\n";

Résultats:

Rand:   2.09621596336
SHA-1:  0.611464977264
MD5:    0.618473052979

Donc, ma suggestion, si vous voulez de la vitesse (mais pas un jeu de caractères complet), est de vous en tenir à MD5, SHA-1 ou Uniqid (que je n'ai pas encore testé ..)

56
St. John Johnson

Aucune des réponses fournies ici n’est suffisante si vous voulez un caractère aléatoire de force cryptographique (un attaquant déterminé tente-t-il de deviner ce que sont vos clés aléatoires?). Le hachage n’est pas sûr, un attaquant peut considérablement accélérer sa recherche en indiquant le moment où il pense que votre serveur a généré la clé, et il est facile de rechercher toutes les millisecondes d’une année donnée, même sur un ordinateur portable peu d'espace de recherche). En outre, il est dangereux de simplement exécuter les résultats de uniqid() ou d'une autre source aléatoire faible via une fonction de hachage afin de "l'étendre" - cela ne rend pas plus difficile la recherche d'un attaquant une fois qu'il a découvert que vous l'avez fait.

Si vous avez vraiment besoin d'une sécurité de niveau crypto, lisez/dev/random, le code suivant devrait fonctionner pour tout système compatible POSIX (autre que Windows):

#Generate a random key from /dev/random
function get_key($bit_length = 128){
    $fp = @fopen('/dev/random','rb');
    if ($fp !== FALSE) {
        $key = substr(base64_encode(@fread($fp,($bit_length + 7) / 8)), 0, (($bit_length + 5) / 6)  - 2);
        @fclose($fp);
        return $key;
    }
    return null;
}

Si vous avez besoin d'un peu plus de vitesse, vous pouvez plutôt lire dans 'dev/urandom'. 

27
SecurityJoe

Vous pouvez toujours utiliser uniqid (), il suffit de faire un traitement supplémentaire pour développer sa valeur jusqu'au nombre de caractères dont vous avez besoin.

Par exemple, pour le développer à 32 caractères, vous pouvez faire

$id = md5(uniqid());

Pour le développer à 64 caractères, ajoutez simplement le md5 du md5, comme

$first = md5(uniqid());
$id = $first . md5($first);

Ensuite, déterminez si nécessaire, si vous avez besoin de moins d’un multiple de 32.

Il est possible que vous soyez confronté à des collisions, mais c'est plutôt improbable. Si vous êtes paranoïaque à ce sujet, utilisez simplement la même idée, mais changez uniqid() à travers un chiffre symétrique comme AES au lieu de le hacher.

8
Bob Somers

Pourquoi ne pas utiliser openssl_random_pseudo_bytes http://www.php.net/manual/en/function.openssl-random-pseudo-bytes.php

3
omar-ali

Ma réponse précédente prenait un hash de uniqid () associé à un sel dérivé de variables système, mais cela ne générait pas de clé aléatoire, seule une clé très susceptible d'être unique. De même, la sortie de Rand () ou de mt_Rand () n'est pas assez aléatoire pour des raisons de cryptographie.

Pour générer une clé aléatoire, vous devez accéder à une bonne source de données aléatoire et la fonction openssl_random_pseudo_bytes () est conçue pour le faire:

$randkey = base64_encode(openssl_random_pseudo_bytes(32));

La base64_encode () doit en faire des caractères imprimables.

La raison pour laquelle les autres réponses ne recommandent pas openssl_random_pseudo_bytes () est probablement due à son introduction dans PHP 5.3, et cette question a presque 6 ans.

1
thomasrutter

Est-ce que cette question pourrait vous intéresser?

Je ne suis pas sûr de savoir pourquoi uniqid() ne fonctionne pas pour vous et dans quel cas vous avez besoin d'un nombre unique exprimé en millisecondes mais pas nécessairement autrement; que produisez-vous si rapidement que dans la même milliseconde, vous pourriez avoir une collision? Je me demande combien de temps il faut à uniqid() pour générer son nombre. Si vous le souhaitez, utilisez le paramètre préfixe de la fonction uniqid() avec quelques lettres aléatoires et vous devriez être en sécurité.

Si c'est pour générer un fichier, vous pouvez regarder tmpfile() ou tempname() .

Dans tous les cas, en fonction de vos objectifs, vous pouvez simplement boucler et vérifier si l'identifiant unique est déjà pris (dans un tableau, avec file_exists, etc.) et en générer un autre si c'est le cas.


De plus, comme je ne suis pas sûr de bien comprendre votre question, je vous renvoie à ces autres questions qui semblent assez similaires pendant que je vois la différence entre vous:

Le premier vous intéressera si vous souhaitez créer un identifiant unique lisible par l'homme. La seconde pourrait être utile si vous voulez jouer avec des nombres aléatoires et md5/sha1. Bien que, encore une fois, je pense que uniqid() soit peut-être déjà ce que vous recherchez.

0
lpfavreau

$ key = md5 (microtime (). Rand ());

Microtime par lui-même n'est pas sûr car il ne laisse que 1/1000 chance d'être deviné. 

Rand en tant que tel n’est pas non plus très sécurisé, mais leur concaténation permet une randomisation assez solide.

0
user266926