meilleure pratique pour générer un token aléatoire pour mot de passe oublié
je veux générer un identifiant pour mot de passe oublié . J'ai lu que je peux le faire en utilisant timestamp avec mt_rand(), mais certaines personnes disent que Time stamp pourrait ne pas être unique à chaque fois. Donc je suis un peu confus ici. Puis-je le faire avec l'aide d'horodatage avec cela ?
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 demandé ici, mais je suis de plus en plus confus après avoir lu différentes opinions des différentes personnes.
6 réponses
en PHP, utiliser random_bytes()
. Raison: vous cherchez le moyen d'obtenir un jeton de rappel de mot de passe, et, s'il s'agit d'un identifiant de connexion unique, alors vous avez réellement une donnée à protéger (qui est-compte d'utilisateur entier)
ainsi, le code sera le suivant:
//$length = 78 etc
$token = bin2hex(random_bytes($length));
mise à jour : les versions précédentes de cette réponse était parler de uniqid()
est incorrect s'il y a une question de sécurité et pas seulement d'unicité. uniqid()
est essentiellement juste microtime()
avec un certain encodage. Il existe des moyens simples pour obtenir des prédictions précises du microtime()
sur votre serveur. Un attaquant peut émettre une demande de réinitialisation de mot de passe et ensuite essayer à travers un couple de jetons probables. Cela est également possible si more_entropy est utilisé, car l'entropie supplémentaire est également faible. Merci à @NikiC et @ScottArciszewski pour avoir souligné ceci.
pour plus de détails, voir
ce qui répond le mieux au hasard
$token = bin2hex(openssl_random_pseudo_bytes(16));
la version antérieure de la réponse acceptée ( md5(uniqid(mt_rand(), true))
) est peu sûre et offre seulement environ 2^60 sorties possibles -- bien à la portée d'une recherche de force brutale dans environ une semaine pour un attaquant à petit budget:
-
mt_rand()
est prévisible (et additionne seulement jusqu'à 31 bits d'entropie) -
uniqid()
additionne seulement 29 bits d'entropie -
md5()
ne pas ajouter de l'entropie, c'est juste le mélange de façon déterministe
comme une clé DES 56-bit peut être forcée en environ 24 heures , et un cas moyen aurait environ 59 bits d'entropie, nous pouvons calculer 2^59 / 2^56 = environ 8 jours. En fonction de la façon dont cette vérification du token est implémentée, il pourrait être possible de laisser pratiquement échapper des informations de synchronisation et d'inférer les premiers n octets d'un token de réinitialisation valide .
puisque la question porte sur les" meilleures pratiques " et commence avec...
je veux générer un identifiant pour mot de passe oublié
...nous pouvons en déduire que ce jeton a des exigences implicites de sécurité. Et lorsque vous ajoutez des exigences de sécurité à un générateur de nombres aléatoires, la meilleure pratique est d'utiliser toujours un générateur de nombres pseudorandom cryptographiquement sécurisé (csprng).
à l'Aide d'un CSPRNG
en PHP 7, vous pouvez utiliser bin2hex(random_bytes($n))
(où $n
est un entier plus grand que 15).
en PHP 5, vous pouvez utiliser random_compat
pour exposer la même API.
alternativement, bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM))
si vous avez ext/mcrypt
installé. Une autre bonne doublure est bin2hex(openssl_random_pseudo_bytes($n))
.
la Séparation de la Recherche du Validateur
en me basant sur mes travaux précédents sur cookies sécurisés "se souvenir de moi en PHP , le seul moyen efficace d'atténuer la fuite temporelle susmentionnée (typiquement introduite par la requête de la base de données) est de séparer la recherche de la validation.
si votre table ressemble à ceci (MySQL)...
CREATE TABLE account_recovery (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT
userid INTEGER(11) UNSIGNED NOT NULL,
token CHAR(64),
expires DATETIME,
PRIMARY KEY(id)
);
... vous devez ajouter une colonne, selector
, comme ainsi:
CREATE TABLE account_recovery (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT
userid INTEGER(11) UNSIGNED NOT NULL,
selector CHAR(16),
token CHAR(64),
expires DATETIME,
PRIMARY KEY(id),
KEY(selector)
);
utiliser un CSPRNG quand un mot de passe réinitialiser le token est émis, envoyer les deux valeurs à l'utilisateur, stocker le sélecteur et un SHA-256 hachage du token aléatoire dans la base de données. Utilisez le sélecteur pour saisir le hachage et L'ID utilisateur, calculez le hachage SHA-256 du token que l'utilisateur fournit avec celui stocké dans la base de données en utilisant hash_equals()
.
Exemple De Code
Génération de un token de réinitialisation en PHP 7 (ou 5.6 avec random_compat) avec PDO:
$selector = bin2hex(random_bytes(8));
$token = random_bytes(32);
$urlToEmail = 'http://example.com/reset.php?'.http_build_query([
'selector' => $selector,
'validator' => bin2hex($token)
]);
$expires = new DateTime('NOW');
$expires->add(new DateInterval('PT01H')); // 1 hour
$stmt = $pdo->prepare("INSERT INTO account_recovery (userid, selector, token, expires) VALUES (:userid, :selector, :token, :expires);");
$stmt->execute([
'userid' => $userId, // define this elsewhere!
'selector' => $selector,
'token' => hash('sha256', $token),
'expires' => $expires->format('Y-m-d\TH:i:s')
]);
vérification du token de réinitialisation fourni par l'utilisateur:
$stmt = $pdo->prepare("SELECT * FROM account_recovery WHERE selector = ? AND expires >= NOW()");
$stmt->execute([$selector]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (!empty($results)) {
$calc = hash('sha256', hex2bin($validator));
if (hash_equals($calc, $results[0]['token'])) {
// The reset token is valid. Authenticate the user.
}
// Remove the token from the DB regardless of success or failure.
}
ces snippets de code ne sont pas des solutions complètes (j'ai écarté la validation d'entrée et les intégrations de cadre), mais ils devraient servir d'exemple de ce qu'il faut faire.
vous pouvez également utiliser DEV_RANDOM, où 128 = 1/2 La longueur du token généré. Le Code ci-dessous génère 256 token.
$token = bin2hex(mcrypt_create_iv(128, MCRYPT_DEV_RANDOM));
cela peut être utile chaque fois que vous avez besoin d'un jeton très aléatoire
<?php
echo mb_strtoupper(strval(bin2hex(openssl_random_pseudo_bytes(16))));
?>
vous pouvez utiliser
echo str_shuffle('ASGDHFfdgfdre5475433fd');