Je souhaite générer un identifiant pour le mot de passe oublié. J'ai lu que je peux le faire en utilisant horodatage avec mt_rand (), mais certaines personnes disent que l'horodatage n'est peut-être pas unique à chaque fois. Donc je suis un peu confus ici. Puis-je le faire en utilisant l'horodatage avec ceci?
Question
Quelle est la meilleure pratique pour générer des jetons aléatoires / uniques de longueur personnalisée?
Je sais qu'il y a beaucoup de questions posées ici, mais je suis de plus en plus confus après avoir lu les opinions différentes des différentes personnes.
Réponses:
En PHP, utilisez
random_bytes()
. Raison: vous cherchez le moyen d'obtenir un jeton de rappel de mot de passe, et, s'il s'agit d'informations d'identification de connexion uniques, vous avez en fait une donnée à protéger (qui est - tout le compte d'utilisateur)Ainsi, le code sera le suivant:
Mise à jour : les versions précédentes de cette réponse faisaient référence
uniqid()
et c'est incorrect s'il y a une question de sécurité et pas seulement d'unicité.uniqid()
est essentiellement justemicrotime()
avec un certain codage. Il existe des moyens simples d'obtenir des prévisions précisesmicrotime()
sur votre serveur. Un attaquant peut émettre une demande de réinitialisation de mot de passe, puis essayer quelques jetons probables. Ceci est également possible si more_entropy est utilisé, car l'entropie supplémentaire est également faible. Merci à @NikiC et @ScottArciszewski pour l'avoir signalé.Pour plus de détails, voir
la source
random_bytes()
n'est disponible qu'à partir de PHP7. Pour les anciennes versions, la réponse de @yesitsme semble être la meilleure option.$length
? L'identifiant de l'utilisateur? Ou quoi?Cela répond à la requête `` meilleur aléatoire '':
La réponse 1 d' Adi de Security.StackExchange a une solution pour cela:
1. Adi, Mon 12 novembre 2018, Celeritas, «Génération d'un jeton impossible à deviner pour les e-mails de confirmation», 20 septembre 2013 à 7 h 06, https://security.stackexchange.com/a/40314/
la source
openssl_random_pseudo_bytes($length)
- support: PHP 5> = 5.3.0, ....................................... ................... (Pour PHP 7 et plus, utilisezrandom_bytes($length)
) ...................... .................... (Pour PHP inférieur à 5.3 - ne pas utiliser PHP inférieur à 5.3)La version antérieure de la réponse acceptée (
md5(uniqid(mt_rand(), true))
) n'est pas sécurisée et n'offre qu'environ 2 ^ 60 sorties possibles - bien dans la portée d'une recherche par force brute dans environ une semaine pour un attaquant à petit budget:mt_rand()
est prévisible (et n'ajoute que 31 bits d'entropie)uniqid()
ajoute seulement jusqu'à 29 bits d'entropiemd5()
n'ajoute pas d'entropie, il la mélange simplement de manière déterministePuisqu'une clé DES de 56 bits peut être forcée brutalement en environ 24 heures , et qu'un cas moyen aurait environ 59 bits d'entropie, nous pouvons calculer 2 ^ 59/2 ^ 56 = environ 8 jours. Selon la façon dont cette vérification de jeton est mise en œuvre, il peut être possible de pratiquement perdre des informations de synchronisation et de déduire les N premiers octets d'un jeton de réinitialisation valide .
Puisque la question porte sur les «bonnes pratiques» et s'ouvre sur ...
... nous pouvons en déduire que ce jeton a des exigences de sécurité implicites. Et lorsque vous ajoutez des exigences de sécurité à un générateur de nombres aléatoires, la meilleure pratique consiste à toujours utiliser un générateur de nombres pseudo-aléatoires cryptographiquement sécurisé (abrégé CSPRNG).
Utilisation d'un CSPRNG
En PHP 7, vous pouvez utiliser
bin2hex(random_bytes($n))
(où$n
est un entier supérieur à 15).En PHP 5, vous pouvez utiliser
random_compat
pour exposer la même API.Sinon,
bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM))
si vous avezext/mcrypt
installé. Un autre bon one-liner estbin2hex(openssl_random_pseudo_bytes($n))
.Séparer la recherche du validateur
En tirant de mon travail précédent sur les cookies sécurisés «se souvenir de moi» en PHP , le seul moyen efficace d'atténuer la fuite de synchronisation susmentionnée (généralement introduite par la requête de base de données) est de séparer la recherche de la validation.
Si votre table ressemble à ceci (MySQL) ...
... vous devez ajouter une colonne supplémentaire
selector
, comme ceci:Utiliser un CSPRNG Lorsqu'un jeton de réinitialisation de mot de passe est émis, envoyez les deux valeurs à l'utilisateur, stockez le sélecteur et un hachage SHA-256 du jeton aléatoire dans la base de données. Utilisez le sélecteur pour récupérer le hachage et l'ID utilisateur, calculez le hachage SHA-256 du jeton fourni par l'utilisateur avec celui stocké dans la base de données
hash_equals()
.Exemple de code
Générer un jeton de réinitialisation en PHP 7 (ou 5.6 avec random_compat) avec PDO:
Vérification du jeton de réinitialisation fourni par l'utilisateur:
Ces extraits de code ne sont pas des solutions complètes (j'ai évité la validation d'entrée et les intégrations de framework), mais ils devraient servir d'exemple de ce qu'il faut faire.
la source
hash('sha256', bin2hex($token))
, 2) vérifier avecif (hash_equals(hash('sha256', $validator), $results[0]['token'])) {...
? Merci!id
comme sélecteur? Je veux dire, la clé primaire de laaccount_recovery
table. Nous n'avons pas besoin d'une couche de sécurité supplémentaire pour le sélecteur, n'est-ce pas? Merci!id:secret
est OK.selector:secret
est OK.secret
lui-même ne l'est pas. Le but est de séparer la requête de la base de données (qui manque de temps) du protocole d'authentification (qui doit être à temps constant).openssl_random_pseudo_bytes
placerandom_bytes
si utilise PHP 5.6? De plus, ne devriez-vous pas ajouter uniquement le sélecteur et non le validateur dans la chaîne de requête du lien?Vous pouvez également utiliser DEV_RANDOM, où 128 = 1/2 de la longueur du jeton généré. Le code ci-dessous génère 256 jetons.
la source
MCRYPT_DEV_URANDOM
plusMCRYPT_DEV_RANDOM
.Cela peut être utile chaque fois que vous avez besoin d'un jeton très aléatoire
la source