कैसे एक पासवर्ड हैश करने के लिए


117

मैं फोन पर पासवर्ड के हैश को स्टोर करना चाहता हूं, लेकिन मुझे यकीन नहीं है कि यह कैसे करना है। मैं केवल एन्क्रिप्शन विधियों को खोजने के लिए प्रतीत हो सकता हूं। पासवर्ड को ठीक से कैसे किया जाना चाहिए?

जवाबों:


62

अद्यतन : इस उत्तर गंभीरता से संबंधित है । कृपया इसके बजाय https://stackoverflow.com/a/10402129/251311 से सिफारिशों का उपयोग करें ।

आप या तो उपयोग कर सकते हैं

var md5 = new MD5CryptoServiceProvider();
var md5data = md5.ComputeHash(data);

या

var sha1 = new SHA1CryptoServiceProvider();
var sha1data = sha1.ComputeHash(data);

dataबाइट सरणी के रूप में प्राप्त करने के लिए आप इसका उपयोग कर सकते हैं

var data = Encoding.ASCII.GetBytes(password);

और से md5dataया वापस स्ट्रिंग प्राप्त करने के लिएsha1data

var hashedPassword = ASCIIEncoding.GetString(md5data);

11
मैं वास्तव में SHA1 का उपयोग करने की सलाह दूंगा। MD5 एक नहीं-नहीं है जब तक कि आप मौजूदा सिस्टम के साथ पिछड़े संगतता को बनाए नहीं रखते। इसके अलावा, सुनिश्चित करें कि आप इसे एक usingबयान में डालते हैं या Clear()उस पर कॉल करते हैं जब आप कार्यान्वयन का उपयोग कर रहे होते हैं।
vcsjones 3

3
@vcsjones: मैं यहां पवित्र-युद्ध नहीं करना चाहता, लेकिन md5लगभग सभी तरह के कार्यों के लिए पर्याप्त है। इसकी भेद्यता बहुत विशिष्ट स्थितियों को संदर्भित करती है और लगभग क्रिप्टोग्राफी के बारे में बहुत कुछ जानने के लिए हमलावर की आवश्यकता होती है।
झटके 3

4
@zerkms बिंदु लिया गया है, लेकिन यदि पिछड़े संगतता का कोई कारण नहीं है, तो एमडी 5 का उपयोग करने का कोई कारण नहीं है। "माफी से अधिक सुरक्षित"।
vcsjones

4
इस बिंदु पर एमडी 5 का उपयोग करने का कोई कारण नहीं है। यह देखते हुए कि गणना का समय महत्वहीन है, मौजूदा सिस्टम के साथ संगतता के अलावा एमडी 5 का उपयोग करने का कोई कारण नहीं है। यहां तक ​​कि अगर MD5 "काफी अच्छा" है, तो उपयोगकर्ता के साथ कहीं अधिक सुरक्षित SHA की कोई कीमत नहीं है। मुझे यकीन है कि zerkms को पता है कि यह टिप्पणी प्रश्नकर्ता के लिए अधिक है।
गेराल्ड डेविस

11
तीन बड़ी गलतियाँ: 1) ASCII चुपचाप असामान्य वर्णों के साथ पासवर्डों को नीचा करता है 2) सादे MD5 / SHA-1 / SHA-2 तेज है। 3) आपको नमक की आवश्यकता है। | इसके बजाय PBKDF2, bcrypt या scrypt का उपयोग करें। PBKDF2 Rfc2898DeriveBytes वर्ग (WP7 पर मौजूद नहीं है तो यकीन है) में सबसे आसान है
कोडइन्कॉउंस

299

यहां के अधिकांश अन्य उत्तर आज की सर्वोत्तम प्रथाओं के साथ कुछ हद तक पुराने हैं। जैसे कि यहाँ PBKDF2 / का उपयोग करके Rfc2898DeriveBytesपासवर्डों को संग्रहीत और सत्यापित करने का अनुप्रयोग है । निम्न कोड इस पोस्ट में एक स्टैंड-अलोन क्लास में है: नमकीन पासवर्ड हैश को स्टोर करने का एक और उदाहरण । मूल बातें वास्तव में आसान हैं, इसलिए यहां इसे तोड़ दिया गया है:

चरण 1 क्रिप्टोग्राफ़िक PRNG के साथ नमक मान बनाएँ:

byte[] salt;
new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]);

चरण 2 Rfc2898DeriveBytes बनाएँ और हैश मान प्राप्त करें:

var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100000);
byte[] hash = pbkdf2.GetBytes(20);

चरण 3 बाद में उपयोग के लिए नमक और पासवर्ड बाइट को मिलाएं:

byte[] hashBytes = new byte[36];
Array.Copy(salt, 0, hashBytes, 0, 16);
Array.Copy(hash, 0, hashBytes, 16, 20);

चरण 4 भंडारण के लिए एक तार में संयुक्त नमक + हैश चालू करें

string savedPasswordHash = Convert.ToBase64String(hashBytes);
DBContext.AddUser(new User { ..., Password = savedPasswordHash });

STEP 5 उपयोगकर्ता द्वारा दर्ज पासवर्ड को एक संग्रहीत पासवर्ड के विरुद्ध सत्यापित करें

/* Fetch the stored value */
string savedPasswordHash = DBContext.GetUser(u => u.UserName == user).Password;
/* Extract the bytes */
byte[] hashBytes = Convert.FromBase64String(savedPasswordHash);
/* Get the salt */
byte[] salt = new byte[16];
Array.Copy(hashBytes, 0, salt, 0, 16);
/* Compute the hash on the password the user entered */
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100000);
byte[] hash = pbkdf2.GetBytes(20);
/* Compare the results */
for (int i=0; i < 20; i++)
    if (hashBytes[i+16] != hash[i])
        throw new UnauthorizedAccessException();

नोट: आपके विशिष्ट एप्लिकेशन की प्रदर्शन आवश्यकताओं के आधार पर, मूल्य 100000को कम किया जा सकता है। न्यूनतम मान लगभग होना चाहिए 10000


8
@ डैनियल मूल रूप से यह पोस्ट अकेले हैश की तुलना में अधिक सुरक्षित उपयोग करने के बारे में है। यदि आपके पास बस एक पासवर्ड हैश है, यहां तक ​​कि नमक के साथ, आपके उपयोगकर्ताओं के पासवर्ड से समझौता किया जा रहा है (और संभावित रूप से बेचा / प्रकाशित किया गया है), इससे पहले कि आपके पास इसे बदलने के लिए कहने का मौका भी हो। डेवलपर के लिए आसान नहीं, हमलावर के लिए कठिन बनाने के लिए उपरोक्त कोड का उपयोग करें।
csharptest.net 3

2
@DatVM नहीं, हर बार जब आप हैश स्टोर करते हैं तो नया नमक। यही कारण है कि यह भंडारण के लिए हैश के साथ संयुक्त है ताकि आप एक पासवर्ड को सत्यापित कर सकें।
csharptest.net

9
@CiprianJijie पूरे बिंदु के लिए सक्षम होने के लिए नहीं है।
csharptest.net

9
यदि कोई व्यक्ति VerifyPassword विधि कर रहा है, यदि आप Linq और बूलियन के लिए एक छोटी कॉल का उपयोग करना चाहते हैं, तो यह करेगा: hash.SequenceEqual (hashBytes.Skip (_haltSize));
जेसु कैस्टिलो

2
@ csharptest.net आप किस प्रकार के सरणी आकार सुझाते हैं? सरणी का आकार वैसे भी सुरक्षा को बहुत प्रभावित करता है? मुझे नहीं पता है कि हैशिंग / क्रिप्टोग्राफी के बारे में बहुत कुछ
lenny

71

Csharptest.net के शानदार जवाब के आधार पर , मैंने इसके लिए एक क्लास लिखी है:

public static class SecurePasswordHasher
{
    /// <summary>
    /// Size of salt.
    /// </summary>
    private const int SaltSize = 16;

    /// <summary>
    /// Size of hash.
    /// </summary>
    private const int HashSize = 20;

    /// <summary>
    /// Creates a hash from a password.
    /// </summary>
    /// <param name="password">The password.</param>
    /// <param name="iterations">Number of iterations.</param>
    /// <returns>The hash.</returns>
    public static string Hash(string password, int iterations)
    {
        // Create salt
        byte[] salt;
        new RNGCryptoServiceProvider().GetBytes(salt = new byte[SaltSize]);

        // Create hash
        var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations);
        var hash = pbkdf2.GetBytes(HashSize);

        // Combine salt and hash
        var hashBytes = new byte[SaltSize + HashSize];
        Array.Copy(salt, 0, hashBytes, 0, SaltSize);
        Array.Copy(hash, 0, hashBytes, SaltSize, HashSize);

        // Convert to base64
        var base64Hash = Convert.ToBase64String(hashBytes);

        // Format hash with extra information
        return string.Format("$MYHASH$V1${0}${1}", iterations, base64Hash);
    }

    /// <summary>
    /// Creates a hash from a password with 10000 iterations
    /// </summary>
    /// <param name="password">The password.</param>
    /// <returns>The hash.</returns>
    public static string Hash(string password)
    {
        return Hash(password, 10000);
    }

    /// <summary>
    /// Checks if hash is supported.
    /// </summary>
    /// <param name="hashString">The hash.</param>
    /// <returns>Is supported?</returns>
    public static bool IsHashSupported(string hashString)
    {
        return hashString.Contains("$MYHASH$V1$");
    }

    /// <summary>
    /// Verifies a password against a hash.
    /// </summary>
    /// <param name="password">The password.</param>
    /// <param name="hashedPassword">The hash.</param>
    /// <returns>Could be verified?</returns>
    public static bool Verify(string password, string hashedPassword)
    {
        // Check hash
        if (!IsHashSupported(hashedPassword))
        {
            throw new NotSupportedException("The hashtype is not supported");
        }

        // Extract iteration and Base64 string
        var splittedHashString = hashedPassword.Replace("$MYHASH$V1$", "").Split('$');
        var iterations = int.Parse(splittedHashString[0]);
        var base64Hash = splittedHashString[1];

        // Get hash bytes
        var hashBytes = Convert.FromBase64String(base64Hash);

        // Get salt
        var salt = new byte[SaltSize];
        Array.Copy(hashBytes, 0, salt, 0, SaltSize);

        // Create hash with given salt
        var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations);
        byte[] hash = pbkdf2.GetBytes(HashSize);

        // Get result
        for (var i = 0; i < HashSize; i++)
        {
            if (hashBytes[i + SaltSize] != hash[i])
            {
                return false;
            }
        }
        return true;
    }
}

उपयोग:

// Hash
var hash = SecurePasswordHasher.Hash("mypassword");

// Verify
var result = SecurePasswordHasher.Verify("mypassword", hash);

एक नमूना हैश यह हो सकता है:

$MYHASH$V1$10000$Qhxzi6GNu/Lpy3iUqkeqR/J1hh8y/h5KPDjrv89KzfCVrubn

जैसा कि आप देख सकते हैं, मैंने आसान उपयोग के लिए हैश में पुनरावृत्तियों को भी शामिल किया है और अगर हमें अपग्रेड करने की आवश्यकता है, तो इसे अपग्रेड करने की संभावना है।


यदि आप .net कोर में रुचि रखते हैं, तो मेरे पास कोड समीक्षा पर एक .net कोर संस्करण भी है ।


1
बस सत्यापित करने के लिए, यदि आप हैशिंग इंजन को अपग्रेड करते हैं तो आप अपने हैश के V1 सेक्शन को बढ़ाएँगे और उसकी कुंजी बंद करेंगे?
माइक कोल

1
हाँ, यही योजना है। फिर आप के आधार पर तय करेगा V1और V2जो सत्यापन विधि की जरूरत है।
ईसाई गोल्हार्ट

उत्तर के लिए धन्यवाद, और वर्ग। मैं इसे लागू कर रहा हूं जैसा कि हम बोलते हैं।
माइक कोल

2
हां @ नेल्सनसिल्वा। वह नमक की वजह से है ।
ईसाई गोल्हार्ट

1
इस कोड की सभी कॉपी / पेस्टिंग (मेरे सहित) के साथ, मुझे आशा है कि कोई बोलता है और यदि कोई मुद्दा इसके साथ मिल जाता है तो पोस्ट संशोधित हो जाती है! :)
पेटीस 16

14

मैं अपने पासवर्ड एन्क्रिप्शन के लिए एक हैश और एक नमक का उपयोग करता हूं (यह वही हैश है जो Asp.Net सदस्यता का उपयोग करता है):

private string PasswordSalt
{
   get
   {
      var rng = new RNGCryptoServiceProvider();
      var buff = new byte[32];
      rng.GetBytes(buff);
      return Convert.ToBase64String(buff);
   }
}

private string EncodePassword(string password, string salt)
{
   byte[] bytes = Encoding.Unicode.GetBytes(password);
   byte[] src = Encoding.Unicode.GetBytes(salt);
   byte[] dst = new byte[src.Length + bytes.Length];
   Buffer.BlockCopy(src, 0, dst, 0, src.Length);
   Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
   HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
   byte[] inarray = algorithm.ComputeHash(dst);
   return Convert.ToBase64String(inarray);
}

16
-1 सादे SHA-1 का उपयोग करने के लिए, जो तेज है। PBKDF2, bcrypt या scrypt जैसे धीमे कुंजी व्युत्पत्ति फ़ंक्शन का उपयोग करें।
कोडइन्चोअस

1
  1. एक नमक बनाएँ,
  2. नमक के साथ हैश पासवर्ड बनाएं
  3. हैश और नमक दोनों को बचाएं
  4. पासवर्ड और नमक के साथ डिक्रिप्ट ... इसलिए डेवलपर्स पासवर्ड को डिक्रिप्ट नहीं करते हैं
public class CryptographyProcessor
{
    public string CreateSalt(int size)
    {
        //Generate a cryptographic random number.
          RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
         byte[] buff = new byte[size];
         rng.GetBytes(buff);
         return Convert.ToBase64String(buff);
    }


      public string GenerateHash(string input, string salt)
      { 
         byte[] bytes = Encoding.UTF8.GetBytes(input + salt);
         SHA256Managed sHA256ManagedString = new SHA256Managed();
         byte[] hash = sHA256ManagedString.ComputeHash(bytes);
         return Convert.ToBase64String(hash);
      }

      public bool AreEqual(string plainTextInput, string hashedInput, string salt)
      {
           string newHashedPin = GenerateHash(plainTextInput, salt);
           return newHashedPin.Equals(hashedInput); 
      }
 }

1

@ csharptest.net के और ईसाई गोल्हार्ट के जवाब बहुत अच्छे हैं, बहुत बहुत धन्यवाद। लेकिन लाखों रिकॉर्ड के साथ उत्पादन पर इस कोड को चलाने के बाद, मुझे पता चला कि मेमोरी लीक है। RNGCryptoServiceProvider और Rfc2898DeriveBytes कक्षाएं IDisposable से ली गई हैं, लेकिन हम उनका निपटान नहीं करते हैं। मैं अपने समाधान को एक उत्तर के रूप में लिखूंगा यदि किसी को निपटाने वाले संस्करण की आवश्यकता है।

public static class SecurePasswordHasher
{
    /// <summary>
    /// Size of salt.
    /// </summary>
    private const int SaltSize = 16;

    /// <summary>
    /// Size of hash.
    /// </summary>
    private const int HashSize = 20;

    /// <summary>
    /// Creates a hash from a password.
    /// </summary>
    /// <param name="password">The password.</param>
    /// <param name="iterations">Number of iterations.</param>
    /// <returns>The hash.</returns>
    public static string Hash(string password, int iterations)
    {
        // Create salt
        using (var rng = new RNGCryptoServiceProvider())
        {
            byte[] salt;
            rng.GetBytes(salt = new byte[SaltSize]);
            using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations))
            {
                var hash = pbkdf2.GetBytes(HashSize);
                // Combine salt and hash
                var hashBytes = new byte[SaltSize + HashSize];
                Array.Copy(salt, 0, hashBytes, 0, SaltSize);
                Array.Copy(hash, 0, hashBytes, SaltSize, HashSize);
                // Convert to base64
                var base64Hash = Convert.ToBase64String(hashBytes);

                // Format hash with extra information
                return $"$HASH|V1${iterations}${base64Hash}";
            }
        }

    }

    /// <summary>
    /// Creates a hash from a password with 10000 iterations
    /// </summary>
    /// <param name="password">The password.</param>
    /// <returns>The hash.</returns>
    public static string Hash(string password)
    {
        return Hash(password, 10000);
    }

    /// <summary>
    /// Checks if hash is supported.
    /// </summary>
    /// <param name="hashString">The hash.</param>
    /// <returns>Is supported?</returns>
    public static bool IsHashSupported(string hashString)
    {
        return hashString.Contains("HASH|V1$");
    }

    /// <summary>
    /// Verifies a password against a hash.
    /// </summary>
    /// <param name="password">The password.</param>
    /// <param name="hashedPassword">The hash.</param>
    /// <returns>Could be verified?</returns>
    public static bool Verify(string password, string hashedPassword)
    {
        // Check hash
        if (!IsHashSupported(hashedPassword))
        {
            throw new NotSupportedException("The hashtype is not supported");
        }

        // Extract iteration and Base64 string
        var splittedHashString = hashedPassword.Replace("$HASH|V1$", "").Split('$');
        var iterations = int.Parse(splittedHashString[0]);
        var base64Hash = splittedHashString[1];

        // Get hash bytes
        var hashBytes = Convert.FromBase64String(base64Hash);

        // Get salt
        var salt = new byte[SaltSize];
        Array.Copy(hashBytes, 0, salt, 0, SaltSize);

        // Create hash with given salt
        using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations))
        {
            byte[] hash = pbkdf2.GetBytes(HashSize);

            // Get result
            for (var i = 0; i < HashSize; i++)
            {
                if (hashBytes[i + SaltSize] != hash[i])
                {
                    return false;
                }
            }

            return true;
        }

    }
}

उपयोग:

// Hash
var hash = SecurePasswordHasher.Hash("mypassword");

// Verify
var result = SecurePasswordHasher.Verify("mypassword", hash);

0

मुझे लगता है कि KeyDerivation.Pbkdf2 का उपयोग करना Rfc2898DeriveBytes से बेहतर है।

उदाहरण और स्पष्टीकरण: ASP.NET कोर में हैश पासवर्ड

using System;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
 
public class Program
{
    public static void Main(string[] args)
    {
        Console.Write("Enter a password: ");
        string password = Console.ReadLine();
 
        // generate a 128-bit salt using a secure PRNG
        byte[] salt = new byte[128 / 8];
        using (var rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(salt);
        }
        Console.WriteLine($"Salt: {Convert.ToBase64String(salt)}");
 
        // derive a 256-bit subkey (use HMACSHA1 with 10,000 iterations)
        string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
            password: password,
            salt: salt,
            prf: KeyDerivationPrf.HMACSHA1,
            iterationCount: 10000,
            numBytesRequested: 256 / 8));
        Console.WriteLine($"Hashed: {hashed}");
    }
}
 
/*
 * SAMPLE OUTPUT
 *
 * Enter a password: Xtw9NMgx
 * Salt: NZsP6NnmfBuYeJrrAKNuVQ==
 * Hashed: /OOoOer10+tGwTRDTrQSoeCxVTFr6dtYly7d0cPxIak=
 */

यह लेख का एक नमूना कोड है। और यह एक न्यूनतम सुरक्षा स्तर है। इसे बढ़ाने के लिए मैं KeyDeritationPrf.HMACSHA1 पैरामीटर के बजाय उपयोग करूंगा

KeyDerivationPrf.HMACSHA256 या KeyDeritationPrf.HMACSHA512।

पासवर्ड हैशिंग पर समझौता न करें। पासवर्ड हैश हैकिंग का अनुकूलन करने के लिए कई गणितीय ध्वनि विधियाँ हैं। परिणाम विनाशकारी हो सकते हैं। एक बार जब कोई पुरुष आपके उपयोगकर्ताओं के पासवर्ड हैश टेबल पर अपने हाथों को प्राप्त कर सकता है, तो उसके लिए पासवर्ड को क्रैक करना आसान होगा क्योंकि दिए गए एल्गोरिदम कमजोर है या कार्यान्वयन गलत है। पासवर्ड क्रैक करने के लिए उसके पास बहुत समय (समय x कंप्यूटर शक्ति) है। पासवर्ड हैशिंग को क्रिप्टोग्राफिक रूप से मजबूत होना चाहिए ताकि "बहुत समय" " अनुचित समय " हो।

जोड़ने के लिए एक और बिंदु

हैश सत्यापन में समय लगता है (और यह अच्छा है)। जब उपयोगकर्ता गलत उपयोगकर्ता नाम दर्ज करता है, तो यह जाँचने में समय नहीं लगता है कि उपयोगकर्ता नाम गलत है। जब उपयोगकर्ता का नाम सही होता है तो हम पासवर्ड सत्यापन शुरू करते हैं - यह अपेक्षाकृत लंबी प्रक्रिया है।

हैकर के लिए यह समझना बहुत आसान होगा कि उपयोगकर्ता मौजूद है या नहीं।

सुनिश्चित करें कि उपयोगकर्ता का नाम गलत होने पर तत्काल उत्तर न दें।

कहने की जरूरत नहीं है: क्या गलत है इसका जवाब कभी न दें। बस सामान्य "क्रेडेंशियल्स गलत हैं"।


1
BTW, पिछला उत्तर stackoverflow.com/a/57508528/11603057 सही और हानिकारक नहीं है। यह हैशिंग का उदाहरण है, पासवर्ड हैशिंग का नहीं। प्रमुख व्युत्पत्ति प्रक्रिया के दौरान छद्म यादृच्छिक फ़ंक्शन का पुनरावृत्तियों होना चाहिए। कोई नहीं है। मैं इस पर टिप्पणी नहीं कर सकता या इसे कम (मेरी कम प्रतिष्ठा) नहीं कर सकता। कृपया बिना सोचे समझे उत्तर न छोड़ें!
अल्बर्ट हनार्स्की
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.