प्रस्तावना
आपकी तालिका परिभाषा के साथ शुरू:
- UserID
- Fname
- Lname
- Email
- Password
- IV
ये परिवर्तन हैं:
- खेतों
Fname
, Lname
और OpenSSLEmail
द्वारा प्रदान की गई, एक सममित सिफर का उपयोग करके एन्क्रिप्ट किया जाएगा ।
IV
क्षेत्र स्टोर करेगा initialisation वेक्टर एन्क्रिप्शन के लिए इस्तेमाल किया। भंडारण आवश्यकताओं का उपयोग सिफर और मोड पर निर्भर करता है; इसके बारे में बाद में।
Password
क्षेत्र एक का उपयोग कर टुकड़ों में बांटा हो जाएगा एक तरह से , पासवर्ड हैश
एन्क्रिप्शन
सिफर और मोड
सर्वश्रेष्ठ एन्क्रिप्शन सिफर और मोड का चयन करना इस उत्तर के दायरे से परे है, लेकिन अंतिम विकल्प एन्क्रिप्शन कुंजी और आरंभीकरण वेक्टर दोनों के आकार को प्रभावित करता है; इस पोस्ट के लिए हम AES-256-CBC का उपयोग करेंगे जिसमें 16 बाइट्स का एक निश्चित ब्लॉक आकार और 16, 24 या 32 बाइट्स का एक महत्वपूर्ण आकार है।
कूटलेखन कुंजी
एक अच्छी एन्क्रिप्शन कुंजी एक बाइनरी बूँद है जो एक विश्वसनीय यादृच्छिक संख्या जनरेटर से उत्पन्न होती है। निम्नलिखित उदाहरण की सिफारिश की जाएगी (> = 5.3):
$key_size = 32; // 256 bits
$encryption_key = openssl_random_pseudo_bytes($key_size, $strong);
// $strong will be true if the key is crypto safe
यह एक या कई बार किया जा सकता है (यदि आप एन्क्रिप्शन कुंजी की एक श्रृंखला बनाना चाहते हैं)। इन्हें यथासंभव निजी रखें।
चतुर्थ
आरंभीकरण वेक्टर एन्क्रिप्शन में यादृच्छिकता जोड़ता है और सीबीसी मोड के लिए आवश्यक है। इन मूल्यों को आदर्श रूप से केवल एक बार (तकनीकी रूप से एक बार एन्क्रिप्शन कुंजी) उपयोग किया जाना चाहिए, इसलिए किसी पंक्ति के किसी भी भाग के लिए एक अद्यतन इसे पुन: उत्पन्न करना चाहिए।
IV उत्पन्न करने में आपकी सहायता के लिए एक फ़ंक्शन प्रदान किया गया है:
$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
उदाहरण
आइए पहले $encryption_key
और का उपयोग करके नाम फ़ील्ड को एन्क्रिप्ट करें $iv
; ऐसा करने के लिए, हमें अपने डेटा को ब्लॉक आकार में पैड करना होगा:
function pkcs7_pad($data, $size)
{
$length = $size - strlen($data) % $size;
return $data . str_repeat(chr($length), $length);
}
$name = 'Jack';
$enc_name = openssl_encrypt(
pkcs7_pad($name, 16), // padded data
'AES-256-CBC', // cipher and mode
$encryption_key, // secret key
0, // options (not used)
$iv // initialisation vector
);
भंडारण आवश्यकताओं
IV की तरह एन्क्रिप्टेड आउटपुट, बाइनरी है; डेटाबेस में इन मानों को संग्रहीत करने के लिए निर्दिष्ट कॉलम प्रकारों जैसे BINARY
या का उपयोग करके पूरा किया जा सकता है VARBINARY
।
आईवी की तरह आउटपुट वैल्यू, बाइनरी है; MySQL में उन मूल्यों को संग्रहीत करने के लिए, उपयोग BINARY
याVARBINARY
स्तंभों पर विचार करें । यदि यह एक विकल्प नहीं है, आप भी बाइनरी डेटा एक टेक्स्ट का प्रतिनिधित्व में का उपयोग कर परिवर्तित कर सकते हैं base64_encode()
या bin2hex()
, ऐसा करने से 100% अधिक संग्रहण स्थान के लिए 33% के बीच की आवश्यकता है।
डिक्रिप्शन
संग्रहीत मूल्यों का डिक्रिप्शन समान है:
function pkcs7_unpad($data)
{
return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$row = $result->fetch(PDO::FETCH_ASSOC); // read from database result
// $enc_name = base64_decode($row['Name']);
// $enc_name = hex2bin($row['Name']);
$enc_name = $row['Name'];
// $iv = base64_decode($row['IV']);
// $iv = hex2bin($row['IV']);
$iv = $row['IV'];
$name = pkcs7_unpad(openssl_decrypt(
$enc_name,
'AES-256-CBC',
$encryption_key,
0,
$iv
));
प्रमाणित एन्क्रिप्शन
आप एक गुप्त कुंजी (एन्क्रिप्शन कुंजी से अलग) और सिफर पाठ से उत्पन्न हस्ताक्षर को जोड़कर उत्पन्न सिफर पाठ की अखंडता में और सुधार कर सकते हैं। इससे पहले कि सिफर पाठ को डिक्रिप्ट किया जाए, हस्ताक्षर पहले सत्यापित किया जाता है (अधिमानतः एक तुलनात्मक विधि के साथ)।
उदाहरण
// generate once, keep safe
$auth_key = openssl_random_pseudo_bytes(32, $strong);
// authentication
$auth = hash_hmac('sha256', $enc_name, $auth_key, true);
$auth_enc_name = $auth . $enc_name;
// verification
$auth = substr($auth_enc_name, 0, 32);
$enc_name = substr($auth_enc_name, 32);
$actual_auth = hash_hmac('sha256', $enc_name, $auth_key, true);
if (hash_equals($auth, $actual_auth)) {
// perform decryption
}
यह सभी देखें: hash_equals()
हैशिंग
अपने डेटाबेस में एक प्रतिवर्ती पासवर्ड संग्रहीत करने से जितना संभव हो उतना बचा जाना चाहिए; आप केवल इसकी सामग्री को जानने के बजाय पासवर्ड को सत्यापित करना चाहते हैं। यदि कोई उपयोगकर्ता अपना पासवर्ड खो देता है, तो उन्हें अपने मूल एक भेजने के बजाय इसे रीसेट करने की अनुमति देना बेहतर है (सुनिश्चित करें कि पासवर्ड रीसेट केवल एक सीमित समय के लिए किया जा सकता है)।
हैश फ़ंक्शन को लागू करना एक तरह से ऑपरेशन है; बाद में इसे मूल डेटा का खुलासा किए बिना सत्यापन के लिए सुरक्षित रूप से उपयोग किया जा सकता है; पासवर्ड के लिए, एक क्रूर बल विधि कई लोगों की अपेक्षाकृत कम लंबाई और खराब पासवर्ड विकल्पों के कारण इसे उजागर करने के लिए एक व्यवहार्य दृष्टिकोण है।
MD5 या SHA1 जैसे हैशिंग एल्गोरिदम एक ज्ञात हैश मान के खिलाफ फ़ाइल सामग्री को सत्यापित करने के लिए बनाए गए थे। वे इस सत्यापन को यथासंभव सटीक बनाने के लिए बहुत अनुकूलित हैं, जबकि अभी भी सटीक है। उनके अपेक्षाकृत सीमित आउटपुट स्पेस को देखते हुए ज्ञात पासवर्ड और उनके संबंधित हैश आउटपुट, इंद्रधनुष तालिकाओं के साथ एक डेटाबेस बनाना आसान था।
हैशिंग से पहले पासवर्ड में नमक जोड़ना एक रेनबो टेबल को बेकार कर देगा, लेकिन हाल ही में हार्डवेयर की प्रगति ने पाशविक बल को एक व्यवहार्य दृष्टिकोण प्रदान किया। इसलिए आपको एक हैशिंग एल्गोरिथ्म की आवश्यकता है जो जानबूझकर धीमा है और बस अनुकूलन करना असंभव है। यह भविष्य के प्रमाण बनाने के लिए मौजूदा पासवर्ड हैश को सत्यापित करने की क्षमता को प्रभावित किए बिना तेजी से हार्डवेयर के लिए लोड को बढ़ाने में सक्षम होना चाहिए।
वर्तमान में दो लोकप्रिय विकल्प उपलब्ध हैं:
- PBKDF2 (पासवर्ड आधारित कुंजी व्युत्पत्ति कार्य v2)
- Bcrypt (उर्फ ब्लोफिश)
यह उत्तर bcrypt के साथ एक उदाहरण का उपयोग करेगा।
पीढ़ी
एक पासवर्ड हैश इस तरह उत्पन्न किया जा सकता है:
$password = 'my password';
$random = openssl_random_pseudo_bytes(18);
$salt = sprintf('$2y$%02d$%s',
13, // 2^n cost factor
substr(strtr(base64_encode($random), '+', '.'), 0, 22)
);
$hash = crypt($password, $salt);
नमक के साथ उत्पन्न होता है openssl_random_pseudo_bytes()
डेटा जो तब के माध्यम से चलाया जाता है के एक यादृच्छिक ब्लॉब बनाने के लिए base64_encode()
और strtr()
के लिए जरूरी वर्णमाला मैच के लिए [A-Za-z0-9/.]
।
crypt()
कार्य एल्गोरिथ्म (के आधार पर हैशिंग $2y$
ब्लोफिश के लिए), लागत कारक और 22 अक्षरों के नमक (13 का एक पहलू एक 3GHz मशीन पर मोटे तौर पर 0.40s लेता है)।
मान्यकरण
एक बार जब आप उपयोगकर्ता की जानकारी वाली पंक्ति प्राप्त कर लेते हैं, तो आप पासवर्ड को इस तरीके से सत्यापित करते हैं:
$given_password = $_POST['password']; // the submitted password
$db_hash = $row['Password']; // field with the password hash
$given_hash = crypt($given_password, $db_hash);
if (isEqual($given_hash, $db_hash)) {
// user password verified
}
// constant time string compare
function isEqual($str1, $str2)
{
$n1 = strlen($str1);
if (strlen($str2) != $n1) {
return false;
}
for ($i = 0, $diff = 0; $i != $n1; ++$i) {
$diff |= ord($str1[$i]) ^ ord($str2[$i]);
}
return !$diff;
}
पासवर्ड को सत्यापित करने के लिए, आप crypt()
फिर से कॉल करते हैं लेकिन आप पहले से गणना की गई हैश को नमक मान के रूप में पास करते हैं। यदि दिया गया पासवर्ड हैश से मेल खाता है, तो रिटर्न वैल्यू उसी हैश की उपज देती है। हैश को सत्यापित करने के लिए, अक्सर समय हमलों से बचने के लिए एक निरंतर-समय तुलना फ़ंक्शन का उपयोग करने की सिफारिश की जाती है।
PHP 5.5 के साथ पासवर्ड हैशिंग
PHP 5.5 ने पासवर्ड हैशिंग कार्यों को प्रस्तुत किया है जिसका उपयोग आप हैशिंग की उपरोक्त विधि को सरल बनाने के लिए कर सकते हैं:
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 13]);
और पुष्टि:
if (password_verify($given_password, $db_hash)) {
// password valid
}
यह भी देखें: password_hash()
,password_verify()