स्वीकृत उत्तर का पूर्व संस्करण ( md5(uniqid(mt_rand(), true))
) असुरक्षित है और केवल 2 ^ 60 संभावित आउटपुट प्रदान करता है - एक कम बजट के हमलावर के लिए लगभग एक सप्ताह के समय में एक क्रूर बल खोज की सीमा के भीतर:
चूंकि 56-बिट डेस कुंजी को लगभग 24 घंटों में क्रूरता-मजबूर किया जा सकता है , और एक औसत मामले में लगभग 59 बिट्स एन्ट्रापी होंगे, हम 2 ^ 59/2 ^ 56 = लगभग 8 दिनों की गणना कर सकते हैं। यह टोकन सत्यापन कैसे लागू किया जाता है, इसके आधार पर, यह संभव हो सकता है कि समय-समय पर जानकारी लीक हो और एक वैध रीसेट टोकन के पहले एन बाइट्स का अनुमान लगाया जाए ।
चूंकि सवाल "सर्वोत्तम प्रथाओं" के बारे में है और इसके साथ खुलता है ...
मैं पासवर्ड भूल गए के लिए पहचानकर्ता उत्पन्न करना चाहता हूं
... हम अनुमान लगा सकते हैं कि इस टोकन में अंतर्निहित सुरक्षा आवश्यकताएं हैं। और जब आप एक यादृच्छिक संख्या जनरेटर के लिए सुरक्षा आवश्यकताओं को जोड़ते हैं, तो सबसे अच्छा अभ्यास हमेशा क्रिप्टोग्राफिक रूप से सुरक्षित छद्म आयामी संख्या जनरेटर (संक्षिप्त सीएसआरएनजी) का उपयोग करना है।
CSPRNG का उपयोग करना
PHP 7 में, आप bin2hex(random_bytes($n))
(जहाँ $n
पूर्णांक 15 से बड़ा है) का उपयोग कर सकते हैं ।
PHP 5 में, आप random_compat
उसी API को एक्सपोज़ करने के लिए उपयोग कर सकते हैं ।
वैकल्पिक रूप से, bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM))
यदि आपने ext/mcrypt
स्थापित किया है। एक और अच्छा वन-लाइनर है bin2hex(openssl_random_pseudo_bytes($n))
।
लुकअप को वैलिडेटर से अलग करना
PHP में सुरक्षित "मुझे याद रखें" कुकीज़ पर अपने पिछले काम से खींचकर , पूर्वोक्त समय रिसाव (आमतौर पर डेटाबेस क्वेरी द्वारा पेश) को कम करने का एकमात्र प्रभावी तरीका है कि लुकअप को सत्यापन से अलग किया जाए।
यदि आपकी तालिका इस प्रकार है (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)
);
... आपको एक और कॉलम जोड़ने की जरूरत है selector
, जैसे:
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 का उपयोग करें जब पासवर्ड रीसेट टोकन जारी किया जाता है, तो उपयोगकर्ता को दोनों मान भेजें, डेटाबेस में यादृच्छिक टोकन के चयनकर्ता और SHA-256 हैश को संग्रहीत करें। हैश और यूजर आईडी को हथियाने के लिए चयनकर्ता का उपयोग करें, उपयोगकर्ता द्वारा प्रदान किए गए टोकन के SHA-256 हैश की गणना डेटाबेस में संग्रहीत एक के साथ करता है hash_equals()
।
उदाहरण कोड
PDO के साथ PHP 7 (या random_compat के साथ 5.6) में एक रीसेट टोकन उत्पन्न करना:
$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')
]);
उपयोगकर्ता द्वारा प्रदान किए गए रीसेट टोकन का सत्यापन:
$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.
}
ये कोड स्निपेट पूर्ण समाधान नहीं हैं (मैंने इनपुट सत्यापन और ढांचे को एकीकृत किया है), लेकिन उन्हें उदाहरण के रूप में काम करना चाहिए।