ठीक है, मुझे यह स्पष्ट रूप से बताने दें: यदि आप उपयोगकर्ता डेटा, या उपयोगकर्ता डेटा से प्राप्त कुछ भी इस उद्देश्य के लिए कुकी में डाल रहे हैं, तो आप कुछ गलत कर रहे हैं।
वहाँ। यह मैंने कहा था। अब हम वास्तविक उत्तर पर आगे बढ़ सकते हैं।
हैशिंग उपयोगकर्ता डेटा में क्या गलत है, आप पूछें? खैर, यह अस्पष्टता के माध्यम से सतह और सुरक्षा के संपर्क में आता है।
एक दूसरे के लिए कल्पना कीजिए कि आप हमलावर हैं। आप अपने सत्र में याद करने के लिए एक क्रिप्टोग्राफ़िक कुकी सेट देखें। यह 32 अक्षर चौड़ा है। जी। यह एक MD5 हो सकता है ...
आइए एक दूसरे के लिए भी कल्पना करें कि वे उस एल्गोरिथ्म को जानते हैं जो आपने उपयोग किया था। उदाहरण के लिए:
md5(salt+username+ip+salt)
अब, सभी हमलावरों को "नमक" (जो वास्तव में एक नमक नहीं है, लेकिन उस पर बाद में अधिक है) को बल देने की ज़रूरत है, और वह अब अपने आईपी पते के लिए किसी भी उपयोगकर्ता नाम के साथ सभी नकली टोकन उत्पन्न कर सकता है! लेकिन एक नमक के लिए मजबूर करना, सही है? पूर्ण रूप से। लेकिन आधुनिक दिन के जीपीयू इस पर बहुत अच्छे हैं। और जब तक आप इसमें पर्याप्त यादृच्छिकता का उपयोग नहीं करते (इसे काफी बड़ा बनाते हैं), यह जल्दी से गिरने वाला है, और इसके साथ आपके महल की चाबी।
संक्षेप में, आपकी रक्षा करने वाली एकमात्र चीज नमक है, जो वास्तव में आपकी रक्षा नहीं कर रही है जितना आप सोचते हैं।
लेकिन रुकें!
उस सभी की भविष्यवाणी की गई थी कि हमलावर एल्गोरिदम जानता है! यदि यह गुप्त और भ्रामक है, तो आप सुरक्षित हैं, है ना? गलत । सोच की उस लाइन का एक नाम है: सिक्योरिटी थ्रू ऑब्सुरिटी , जिस पर कभी भरोसा नहीं करना चाहिए ।
बेहतर तरीका है
बेहतर तरीका यह है कि यूजर की जानकारी को कभी भी सर्वर को न छोड़ें, केवल आईडी को छोड़कर।
जब उपयोगकर्ता लॉग इन करता है, तो एक बड़ा (128 से 256 बिट) यादृच्छिक टोकन उत्पन्न करें। उस डेटाबेस तालिका में जोड़ें, जो उपयोगकर्ता के लिए टोकन मैप करती है, और फिर इसे क्लाइंट को कुकी में भेजती है।
क्या होगा यदि हमलावर किसी अन्य उपयोगकर्ता के यादृच्छिक टोकन का अनुमान लगाता है?
अच्छा, चलो यहाँ कुछ गणित करते हैं। हम एक 128 बिट यादृच्छिक टोकन उत्पन्न कर रहे हैं। इसका मतलब है कि वहाँ हैं:
possibilities = 2^128
possibilities = 3.4 * 10^38
अब, यह दिखाने के लिए कि यह संख्या कितनी बेतुकी है, आइए इंटरनेट पर हर सर्वर की कल्पना करें (चलो आज 50,000,000 कहते हैं) उस संख्या को प्रत्येक सेकंड 1,000,000,000 की दर से बल देने की कोशिश कर रहे हैं। वास्तव में आपके सर्वर ऐसे लोड के तहत पिघल जाएंगे, लेकिन चलो इसे खेलते हैं।
guesses_per_second = servers * guesses
guesses_per_second = 50,000,000 * 1,000,000,000
guesses_per_second = 50,000,000,000,000,000
तो 50 क्वाड्रिलियन प्रति सेकंड का अनुमान है। वह तेज है! सही?
time_to_guess = possibilities / guesses_per_second
time_to_guess = 3.4e38 / 50,000,000,000,000,000
time_to_guess = 6,800,000,000,000,000,000,000
तो 6.8 sextillion सेकंड ...
आइए इसे और अधिक अनुकूल संख्या में लाने का प्रयास करें।
215,626,585,489,599 years
या इससे भी बेहतर:
47917 times the age of the universe
हाँ, यह ब्रह्मांड की आयु का 47917 गुना है ...
असल में, यह फटा नहीं जा रहा है।
इसलिए योग करने के लिए:
बेहतर तरीका जो मैं सुझाता हूं वह कुकी को तीन भागों के साथ संग्रहीत करना है।
function onLogin($user) {
$token = GenerateRandomToken(); // generate a token, should be 128 - 256 bit
storeTokenForUser($user, $token);
$cookie = $user . ':' . $token;
$mac = hash_hmac('sha256', $cookie, SECRET_KEY);
$cookie .= ':' . $mac;
setcookie('rememberme', $cookie);
}
फिर, मान्य करने के लिए:
function rememberMe() {
$cookie = isset($_COOKIE['rememberme']) ? $_COOKIE['rememberme'] : '';
if ($cookie) {
list ($user, $token, $mac) = explode(':', $cookie);
if (!hash_equals(hash_hmac('sha256', $user . ':' . $token, SECRET_KEY), $mac)) {
return false;
}
$usertoken = fetchTokenByUserName($user);
if (hash_equals($usertoken, $token)) {
logUserIn($user);
}
}
}
नोट: उपयोगकर्ता और टोकन के संयोजन का उपयोग न करें और अपने डेटाबेस में रिकॉर्ड देखने के लिए टोकन। हमेशा उपयोगकर्ता के आधार पर एक रिकॉर्ड प्राप्त करना सुनिश्चित करें और बाद में प्राप्त टोकन की तुलना करने के लिए एक समय-सुरक्षित तुलना फ़ंक्शन का उपयोग करें। समय हमलों के बारे में अधिक ।
अब, यह बहुत महत्वपूर्ण है कि SECRET_KEY
एक क्रिप्टोग्राफिक रहस्य ( /dev/urandom
किसी उच्च-एन्ट्रापी इनपुट से उत्पन्न और / या व्युत्पन्न कुछ द्वारा उत्पन्न ) हो। इसके अलावा, GenerateRandomToken()
एक मजबूत यादृच्छिक स्रोत होने की आवश्यकता है ( mt_rand()
लगभग इतना मजबूत नहीं है। एक पुस्तकालय का उपयोग करें, जैसे कि randomLib या random_compat , या के mcrypt_create_iv()
साथ DEV_URANDOM
...)
hash_equals()
रोकने के लिए है समय हमलों । यदि आप PHP 5.6 के नीचे PHP संस्करण का उपयोग करते हैं, तो फ़ंक्शन hash_equals()
समर्थित नहीं है। इस स्थिति में आप hash_equals()
टाइमसेफ़.कॉम फ़ंक्शन को बदल सकते हैं :
/**
* A timing safe equals comparison
*
* To prevent leaking length information, it is important
* that user input is always used as the second parameter.
*
* @param string $safe The internal (safe) value to be checked
* @param string $user The user submitted (unsafe) value
*
* @return boolean True if the two strings are identical.
*/
function timingSafeCompare($safe, $user) {
if (function_exists('hash_equals')) {
return hash_equals($safe, $user); // PHP 5.6
}
// Prevent issues if string length is 0
$safe .= chr(0);
$user .= chr(0);
// mbstring.func_overload can make strlen() return invalid numbers
// when operating on raw binary strings; force an 8bit charset here:
if (function_exists('mb_strlen')) {
$safeLen = mb_strlen($safe, '8bit');
$userLen = mb_strlen($user, '8bit');
} else {
$safeLen = strlen($safe);
$userLen = strlen($user);
}
// Set the result to the difference between the lengths
$result = $safeLen - $userLen;
// Note that we ALWAYS iterate over the user-supplied length
// This is to prevent leaking length information
for ($i = 0; $i < $userLen; $i++) {
// Using % here is a trick to prevent notices
// It's safe, since if the lengths are different
// $result is already non-0
$result |= (ord($safe[$i % $safeLen]) ^ ord($user[$i]));
}
// They are only identical strings if $result is exactly 0...
return $result === 0;
}