Kabul edilen cevabın ( md5(uniqid(mt_rand(), true))
) önceki versiyonu güvensizdir ve yalnızca yaklaşık 2 ^ 60 olası çıktı sunar - düşük bütçeli bir saldırgan için yaklaşık bir hafta içinde bir kaba kuvvet araması aralığı dahilinde:
Bir yana 56 bit DES anahtarı olabilir yaklaşık 24 saat içinde kaba zorla ve ortalama vaka entropi 59 ilgili bit olurdu, 8 gün yaklaşık 2 ^ 59/2 ^ 56 = hesaplayabilir. Bu simge doğrulamasının nasıl uygulandığına bağlı olarak, zamanlama bilgisini pratik olarak sızdırmak ve geçerli bir sıfırlama simgesinin ilk N baytını çıkarmak mümkün olabilir .
Soru "en iyi uygulamalar" ile ilgili olduğu ve şununla açıldığı için ...
Unuttuğum şifre için tanımlayıcı oluşturmak istiyorum
... bu belirtecin örtük güvenlik gereksinimlerine sahip olduğu sonucuna varabiliriz. Ve bir rasgele sayı üretecine güvenlik gereksinimleri eklediğinizde, en iyi uygulama, her zaman kriptografik açıdan güvenli bir sözde rasgele sayı üreteci (kısaltılmış CSPRNG) kullanmaktır.
CSPRNG kullanma
PHP 7'de bin2hex(random_bytes($n))
(burada $n
15'ten büyük bir tam sayı) kullanabilirsiniz.
PHP 5'te, random_compat
aynı API'yi açığa çıkarmak için kullanabilirsiniz .
Alternatif olarak, bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM))
sen varsa ext/mcrypt
yüklü. Bir başka iyi tek astar bin2hex(openssl_random_pseudo_bytes($n))
.
Aramayı Doğrulayıcıdan Ayırma
PHP'de güvenli "beni hatırla" tanımlama bilgileriyle ilgili önceki çalışmamdan yola çıkarak, yukarıda belirtilen zamanlama sızıntısını azaltmanın tek etkili yolu (genellikle veritabanı sorgusu tarafından ortaya konur) aramayı doğrulamadan ayırmaktır.
Tablonuz böyle görünüyorsa (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)
);
... bunun selector
gibi bir sütun daha eklemeniz gerekiyor :
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)
);
CSPRNG kullanma Bir parola sıfırlama belirteci verildiğinde, kullanıcıya her iki değeri de gönderin, seçiciyi ve rastgele belirtecin SHA-256 karmasını veritabanında saklayın. Karma ve Kullanıcı Kimliğini almak için seçiciyi kullanın, kullanıcının kullanarak veritabanında depolanan jetonun SHA-256 karmasını hesaplayın hash_equals()
.
Örnek Kod
PDO ile PHP 7'de (veya random_compat ile 5.6'da) bir sıfırlama belirteci oluşturmak:
$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'));
$stmt = $pdo->prepare("INSERT INTO account_recovery (userid, selector, token, expires) VALUES (:userid, :selector, :token, :expires);");
$stmt->execute([
'userid' => $userId,
'selector' => $selector,
'token' => hash('sha256', $token),
'expires' => $expires->format('Y-m-d\TH:i:s')
]);
Kullanıcı tarafından sağlanan sıfırlama jetonunu doğrulama:
$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'])) {
}
}
Bu kod parçacıkları eksiksiz çözümler değildir (girdi doğrulama ve çerçeve entegrasyonlarından kaçındım), ancak ne yapılması gerektiğine dair bir örnek olarak hizmet etmeleri gerekir.