Что такое алгоритм хеширования по умолчанию, который использует членство в ASP.NET?

Что такое алгоритм хеширования по умолчанию, который использует членство в ASP.NET? И как я могу это изменить?

6 ответов

Решение

РЕДАКТИРОВАТЬ: не используйте поставщика членства как есть, потому что он ужасно неадекватен с точки зрения защиты паролей пользователей

В свете того факта, что поиск в Google "алгоритма хеширования поставщика членства" приводит к получению этого ответа в качестве первого результата, а также к выводу Евангелия, я должен предупредить людей об использовании поставщика членства, подобного этому, и использовании хешей, подобных SHA-1., MD5 и т. Д., Чтобы запутать пароли в базах данных.

ТЛ; др

Используйте функцию получения ключа, такую ​​как bcrypt, scrypt или (если вам требуется соответствие FIPS) PBKDF2 с рабочим фактором, достаточным для того, чтобы время хеширования для одного пароля было близко к 1000 мс или более.

В наши дни хэши легко перебрать с помощью многочисленных примеров взлома данных в недавней истории. Чтобы предотвратить попадание паролей вашего пользователя на pastebin при следующем взломе, убедитесь, что пароли хешируются с помощью функции, которая занимает достаточно много времени для вычисления!

Вместо поставщика членства, попробуйте IdentityReboot или более новые реализации от Microsoft, о которых по крайней мере говорит Трой Хант.

Также интересно, что на тех же результатах Google, упомянутых выше, я нахожу учебное пособие, показывающее людям, как просто перебор этих хэшей паролей с помощью популярных инструментов, таких как JtR или Hashcat. На специализированном графическом процессоре SHA1 может быть взломан со скоростью 48867 миллионов хэшей в секунду! Благодаря бесплатному словарю вроде rockyou и т. П. Мотивированный человек с вашей базой данных очень быстро получит большинство паролей ваших пользователей. Как разработчик, ваша этическая ответственность заключается в том, чтобы делать все необходимое для защиты безопасности паролей ваших пользователей.


Хеширование по умолчанию - SHA1, но они также солят это и base64 это:

public string EncodePassword(string pass, string salt)
{
    byte[] bytes = Encoding.Unicode.GetBytes(pass);
    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);
}

Если вы хотите узнать больше о том, как изменить его, мне все равно нужно это выяснить (если только вы не пользуетесь специальным провайдером, см. Ниже), однако SHA-1 сейчас довольно хорош. Если вы хотите изменить это или посмотреть на это, эти ребята поработали над этим: http://forums.asp.net/p/1336657/2899172.aspx

Этот вопрос SO поможет изменить или дублировать эту технику, если это то, что может понадобиться. Переопределение членства в ASP.NET и хеширования паролей пользователей в Ruby

Если вы делаете собственного провайдера, вы можете создавать свои алгоритмы и методы хеширования и шифрования.

private byte[] ConvertPasswordForStorage(string Password)
      {
         System.Text.UnicodeEncoding ue = 
      new System.Text.UnicodeEncoding();
         byte[] uePassword = ue.GetBytes(Password);
         byte[] RetVal = null;
         switch (_PasswordFormat)
         {
            case MembershipPasswordFormat.Clear:
               RetVal = uePassword;
               break;
            case MembershipPasswordFormat.Hashed:

               HMACSHA1 SHA1KeyedHasher = new HMACSHA1();
               SHA1KeyedHasher.Key = _ValidationKey;
               RetVal = SHA1KeyedHasher.ComputeHash(uePassword);
               break;
            case MembershipPasswordFormat.Encrypted:
               TripleDESCryptoServiceProvider tripleDes = new 
       TripleDESCryptoServiceProvider();
               tripleDes.Key = _DecryptionKey;
               tripleDes.IV = new byte[8];
               MemoryStream mStreamEnc = new MemoryStream();
               CryptoStream cryptoStream = new CryptoStream(mStreamEnc, 
        tripleDes.CreateEncryptor(), 
      CryptoStreamMode.Write);

               cryptoStream.Write(uePassword, 0, uePassword.Length);
               cryptoStream.FlushFinalBlock();
               RetVal = mStreamEnc.ToArray();
               cryptoStream.Close();
               break;

         }
         return RetVal;
      }

private string GetHumanReadablePassword(byte[] StoredPassword)
      {
         System.Text.UnicodeEncoding ue = new System.Text.UnicodeEncoding();
         string RetVal = null;
         switch (_PasswordFormat)
         {
            case MembershipPasswordFormat.Clear:
               RetVal = ue.GetString(StoredPassword);
               break;
            case MembershipPasswordFormat.Hashed:
               throw new ApplicationException(
        "Password cannot be recovered from a hashed format");

            case MembershipPasswordFormat.Encrypted:
               TripleDESCryptoServiceProvider tripleDes = 
        new TripleDESCryptoServiceProvider();
               tripleDes.Key = _DecryptionKey;
               tripleDes.IV = new byte[8];
               CryptoStream cryptoStream = 
        new CryptoStream(new MemoryStream(StoredPassword), 
      tripleDes.CreateDecryptor(), CryptoStreamMode.Read);
               MemoryStream msPasswordDec = new MemoryStream();
               int BytesRead = 0;
               byte[] Buffer = new byte[32];
               while ((BytesRead = cryptoStream.Read(Buffer, 0, 32)) > 0)
               {
                  msPasswordDec.Write(Buffer, 0, BytesRead);

               }
               cryptoStream.Close();

               RetVal = ue.GetString(msPasswordDec.ToArray());
               msPasswordDec.Close();
               break;
         }
         return RetVal;
      }

http://msdn.microsoft.com/en-us/library/aa479048.aspx

Приведенный выше ответ Райана Кристенсена не является полным. Часть, в которой он преобразует соль в байт [], неверна.

Это рабочий пример, который я реализовал в решении для клиента:

public string Hash(string value, string salt)
    {
        byte[] bytes = Encoding.Unicode.GetBytes(value);
        byte[] src = Convert.FromBase64String(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);
    }

Тип алгоритма хеширования по умолчанию - SHA1. Есть два способа изменить это.

1) Если вы работаете с IIS 7, вы можете обновить его, используя конфигурацию "Ключ компьютера" (см. Ниже). Это позволяет выбрать метод шифрования из списка доступных параметров и указать ключи или параметры генерации ключей.

Страница конфигурации ключа компьютера из инструмента администрирования IIS 7

2) Если вы работаете с IIS 6, вы можете изменить тип алгоритма хеширования, используя элемент members в файле web.config:

<membership
    defaultProvider="provider name"
    userIsOnlineTimeWindow="number of minutes"
    hashAlgorithmType="SHA1">
    <providers>...</providers>
</membership>

Согласно документации, строковое значение атрибута hashAlgorithmType может быть любым из предоставленных типов алгоритма хеширования.Net. Немного копания показывает, что действительные значения для ASP.Net 2, 3 и 3.5 MD5, RIPEMD160, SHA1, SHA256, SHA384, SHA512, Важной частью здесь является то, что все эти классы наследуются от HashAlgorithm,

Значением атрибута hashAlgorithmType также может быть запись из элемента cryptoNameMapping в файле machine.config. Вы можете использовать это, если вам требуется сторонний алгоритм хеширования. Файл machine.config обычно находится в C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG если вы используете ASP.Net 2 или более поздней версии. Вы можете прочитать больше о настройке этих значений здесь.

Алгоритм хэширования по умолчанию изменен на HMACSHA256 в.NET 4.0 Framework.

Обратите внимание, что в отличие от SHA-1, HMAC SHA-256 является хешированием с ключами. Если ваши хеши ведут себя недетерминированно, вы, вероятно, не установили ключ, заставляя его использовать случайный ключ. Нечто похожее на следующее было бы виновником (вот что я потратил час, чтобы выяснить:p).

HashAlgorithm.Create(Membership.HashAlgorithmType)

Если вы хотите, чтобы он работал с существующим провайдером, вы можете вернуть его к прежним настройкам по умолчанию, используя это руководство.

В алгоритме хеширования есть одна поправка, которую вы должны использовать:

byte[] src = Convert.FromBase64String(salt);

вместо

byte[] src = Encoding.Unicode.GetBytes(salt);

Читать статью http://svakodnevnica.com.ba/index.php?option=com_kunena&func=view&catid=4&id=4&Itemid=5&lang=en

Давайте обсудим ответы на этот вопрос, которые надежны и проверены временем:

  1. Zetetic Всего две строчки кода и готово! Алгоритм хеширования PBKDF2 намного лучше, чем SHA1 или SHA256-SHA512 и т. Д. Последние алгоритмы, такие как PBKDF2, SCRYPT или ARGON2, являются лидерами, когда дело доходит до хеширования. Но в этом случае полезно использовать PBKDF2, поскольку он реализован.NET вRfc2898DeriveBytesкласс. До сих пор использование этой библиотеки было отличным, но есть некоторые незначительные проблемы, такие как:

    а. По умолчанию Zetetic использует 5000 итераций. Настраивается, если вы используетеPbkdf2Hash256K

    б. Zetetic использованиеRfc2898DeriveBytes а также Rfc2898DeriveBytes основан на HMACSHA1 почему-то и подгонять нельзя.

  2. Хорошие новости! Я настроилRfc2898DeriveBytes использовать HMACSHA512с 128 000 итераций, чтобы SQLMembershipProvider мог использовать PBKDF2, который до сих пор был недоступен. Для этого я объединил код Zetetic с моей реализациейRfc2898DeriveBytes как показано ниже:

using System.Security.Cryptography;

namespace custom.hashing.keyderivation
{
/// <summary>
/// This derived class of PBKDF2Hash provided necessary capabilities to SQLMembershipProvider in order to hash passwords in PBKDF2 way with 128,000 iterations.
/// </summary>
public class PBKDF2Hash : KeyedHashAlgorithm
{
    private const int kHashBytes = 64;

    private System.IO.MemoryStream _ms;

    public int WorkFactor { get; set; }

    public PBKDF2Hash()
        : base()
    {
        this.WorkFactor = 128000;
        this.Key = new byte[32]; // 32 Bytes will give us 256 bits.
        using (var rngCsp = new RNGCryptoServiceProvider())
        {
            // Fill the array with cryptographically secure random bytes.
            rngCsp.GetBytes(this.Key);
        }
    }

    /// <summary>
    /// Hash size in bits
    /// </summary>
    public override int HashSize
    {
        get
        {
            return kHashBytes * 8;
        }
    }

    protected override void HashCore(byte[] array, int ibStart, int cbSize)
    {
        (_ms = _ms ?? new System.IO.MemoryStream()).Write(array, ibStart, cbSize);
    }

    protected override byte[] HashFinal()
    {
        if (this.Key == null || this.Key.Length == 0)
        {
            throw new CryptographicException("Missing KeyedAlgorithm key");
        }

        _ms.Flush();

        var arr = _ms.ToArray();

        _ms = null;

        using (var hmac = new HMACSHA512())
        {
            return new MyRfc2898DeriveBytes(arr, this.Key, this.WorkFactor, hmac).GetBytes(kHashBytes);
        }
    }

    public override void Initialize()
    {
        _ms = null;
    }
}

// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
// <OWNER>Microsoft</OWNER>
// 

//
// Rfc2898DeriveBytes.cs
//

// This implementation follows RFC 2898 recommendations. See http://www.ietf.org/rfc/Rfc2898.txt
/// <summary>
/// Microsoft has implemented PBKDF2 but with HMACSHA1. We are customizing this class to use HMACSHA512 in hashing process.
/// </summary>
public class MyRfc2898DeriveBytes : DeriveBytes
{
    private byte[] m_buffer;
    private byte[] m_salt;
    private HMAC m_hmac;  // The pseudo-random generator function used in PBKDF2

    private uint m_iterations;
    private uint m_block;
    private int m_startIndex;
    private int m_endIndex;

    private int m_blockSize;

    //
    // public constructors
    //

    // This method needs to be safe critical, because in debug builds the C# compiler will include null
    // initialization of the _safeProvHandle field in the method.  Since SafeProvHandle is critical, a
    // transparent reference triggers an error using PasswordDeriveBytes.
    [SecuritySafeCritical]
    public MyRfc2898DeriveBytes(byte[] password, byte[] salt, int iterations, HMAC hmac)
    {
        Salt = salt;
        IterationCount = iterations;
        hmac.Key = password;
        m_hmac = hmac;
        // m_blockSize is in bytes, HashSize is in bits. 
        m_blockSize = hmac.HashSize >> 3;

        Initialize();
    }

    //
    // public properties
    //

    public int IterationCount
    {
        get { return (int)m_iterations; }
        set
        {
            if (value <= 0)
                throw new ArgumentOutOfRangeException("value", "Error: Iteration count is zero or less");

            m_iterations = (uint)value;
            Initialize();
        }
    }

    public byte[] Salt
    {
        get { return (byte[])m_salt.Clone(); }
        set
        {
            if (value == null)
                throw new ArgumentNullException("value");
            if (value.Length < 8)
                throw new ArgumentException("Error: Salt size is less than 8");

            m_salt = (byte[])value.Clone();
            Initialize();
        }
    }

    //
    // public methods
    //

    public override byte[] GetBytes(int cb)
    {
        if (cb <= 0)
        {    throw new ArgumentOutOfRangeException("cb", "Error: Hash size is zero or less"); }

        Contract.Assert(m_blockSize > 0);

        byte[] password = new byte[cb];

        int offset = 0;
        int size = m_endIndex - m_startIndex;
        if (size > 0)
        {
            if (cb >= size)
            {
                Buffer.BlockCopy(m_buffer, m_startIndex, password, 0, size);
                m_startIndex = m_endIndex = 0;
                offset += size;
            }
            else
            {
                Buffer.BlockCopy(m_buffer, m_startIndex, password, 0, cb);
                m_startIndex += cb;
                return password;
            }
        }

        Contract.Assert(m_startIndex == 0 && m_endIndex == 0, "Invalid start or end index in the internal buffer.");

        while (offset < cb)
        {
            byte[] T_block = Func();
            int remainder = cb - offset;
            if (remainder > m_blockSize)
            {
                Buffer.BlockCopy(T_block, 0, password, offset, m_blockSize);
                offset += m_blockSize;
            }
            else
            {
                Buffer.BlockCopy(T_block, 0, password, offset, remainder);
                offset += remainder;
                Buffer.BlockCopy(T_block, remainder, m_buffer, m_startIndex, m_blockSize - remainder);
                m_endIndex += (m_blockSize - remainder);
                return password;
            }
        }
        return password;
    }

    public override void Reset()
    {
        Initialize();
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);

        if (disposing)
        {
            if (m_hmac != null)
            {
                ((IDisposable)m_hmac).Dispose();
            }

            if (m_buffer != null)
            {
                Array.Clear(m_buffer, 0, m_buffer.Length);
            }
            if (m_salt != null)
            {
                Array.Clear(m_salt, 0, m_salt.Length);
            }
        }
    }

    private void Initialize()
    {
        if (m_buffer != null)
            Array.Clear(m_buffer, 0, m_buffer.Length);
        m_buffer = new byte[m_blockSize];
        m_block = 1;
        m_startIndex = m_endIndex = 0;
    }

    internal static byte[] GetBytesFromInt(uint i)
    {
        return unchecked(new byte[] { (byte)(i >> 24), (byte)(i >> 16), (byte)(i >> 8), (byte)i });
    }

    // This function is defined as follow :
    // Func (S, i) = HMAC(S || i) | HMAC2(S || i) | ... | HMAC(iterations) (S || i) 
    // where i is the block number.
    private byte[] Func()
    {
        byte[] INT_block = GetBytesFromInt(m_block);

        m_hmac.TransformBlock(m_salt, 0, m_salt.Length, null, 0);
        m_hmac.TransformBlock(INT_block, 0, INT_block.Length, null, 0);
        m_hmac.TransformFinalBlock(new byte[0], 0, 0);
        byte[] temp = m_hmac.Hash;
        m_hmac.Initialize();

        byte[] ret = temp;
        for (int i = 2; i <= m_iterations; i++)
        {
            m_hmac.TransformBlock(temp, 0, temp.Length, null, 0);
            m_hmac.TransformFinalBlock(new byte[0], 0, 0);
            temp = m_hmac.Hash;
            for (int j = 0; j < m_blockSize; j++)
            {
                ret[j] ^= temp[j];
            }
            m_hmac.Initialize();
        }

        // increment the block count.
        if (m_block == uint.MaxValue)
        { throw new InvalidOperationException("Derived key too long."); }

        m_block++;
        return ret;
    }
}

После создания этого класса сделайте следующее:

  • Добавьте следующую строку в событие Application_Start Global.asax или соответствующий файл запуска вашего проекта:

    System.Security.Cryptography.CryptoConfig.AddAlgorithm(typeof(custom.hashing.keyderivation.PBKDF2Hash), "PBKDF2Hash_HB");

  • И измените web.config как:

    <membership defaultProvider="sitecore" hashAlgorithmType="PBKDF2Hash_HB">

Ссылки для построения этого ответа взяты из:

Я прилагаю фрагмент, показывающий код, как в ответе Роберта выше в F#

open System
open System.Security.Cryptography
open System.Text

module PasswordHelper =
    let EncodePassword(pass : string, salt : string) =
        let bytes = Encoding.Unicode.GetBytes(pass)
        let src = Convert.FromBase64String(salt)
        let dst : byte array = Array.zeroCreate (src.Length + bytes.Length)
        Buffer.BlockCopy(src, 0, dst, 0, src.Length)
        Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length)
        let algorithm = HashAlgorithm.Create("SHA1")
        let inArray = algorithm.ComputeHash(dst)
        Convert.ToBase64String(inArray)

Это рабочий код из активного приложения

Другие вопросы по тегам