ASP.NET成员资格使用的默认哈希算法是什么?我该如何改变呢?
答案 0 :(得分:46)
鉴于googling "membership provider hashing algorithm"将这个答案作为第一个结果,以及将被推断出来的福音这一事实,我应该警告人们使用像这样的成员资格提供者并使用像SHA这样的哈希值1,MD5等模糊数据库中的密码。
Use a key-derivation function like bcrypt, scrypt or (if you need FIPS compliance) PBKDF2,其工作因素足以使单个密码的散列时间达到1000毫秒或更长。
近来历史上有大量数据泄露的例子,哈希很容易蛮力。为了防止用户的密码在下一次黑客攻击中结束使用pastebin,请确保使用足够长的时间进行计算的函数对密码进行哈希处理!
取代会员提供商,至少尝试IdentityReboot或newer implementations from Microsoft that Troy Hunt talks about。
同样有趣的是,在上面提到的相同谷歌搜索结果中,我发现tutorial showing folks preciously how easy it is使用JtR或Hashcat等流行工具来强制使用这些密码哈希值。在自定义GPU装备上,SHA1可以在staggering rate of 48867 million hashes per second!处破解。使用免费字典rockyou or the like,有数据库的有动力的人很快就会拥有大部分用户密码。作为开发人员,做出保护用户密码安全所必需的道德责任是您的道德责任。
默认散列是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问题将有助于扭转或复制这种技术。 Reimplement ASP.NET Membership and User Password Hashing in 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;
}
答案 1 :(得分:30)
above answer by Ryan Christensen未完成。它将盐转换为byte []的部分不正确。
这是我在客户端解决方案中实现的一个工作示例:
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);
}
答案 2 :(得分:27)
默认的哈希算法类型是SHA1。有两种方法可以改变它。
1)如果您使用的是IIS 7,可以使用“机器密钥”配置对其进行更新(如下所示)。这允许您从可用选项列表中选择加密方法,并指定键或密钥生成选项。
2)如果您正在使用IIS 6,则可以使用web.config文件中的membership元素更改哈希算法类型:
<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
,{ {1}}。这里重要的部分是所有这些类都继承自SHA512
。
hashAlgorithmType属性的值也可以是machine.config文件中cryptoNameMapping元素的条目。如果您需要第三方哈希算法,则可以使用此方法。如果您使用的是ASP.Net 2或更高版本,则通常可以在HashAlgorithm
中找到machine.config文件。您可以详细了解如何设置这些值here。
答案 3 :(得分:26)
默认哈希算法在.NET 4.0 Framework中更改为HMACSHA256。
请注意,与SHA-1不同,HMAC SHA-256是一个键控哈希。如果您的哈希行为不确定,您可能没有设置密钥,强制它使用随机密钥。类似于以下的东西将是罪魁祸首(这是我花了一个小时搞清楚:p)。
HashAlgorithm.Create(Membership.HashAlgorithmType)
如果您希望将其与现有提供商配合使用,则可以使用this guide将其还原为以前的默认设置。
答案 4 :(得分:2)
哈希算法中有一个更正,您必须使用:
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#6
答案 5 :(得分:1)
让我们讨论这个问题的答案,这些答案是安全且经过时间检验的:
Zetetic 仅两行代码就完成了!散列算法PBKDF2比拥有SHA1或SHA256-SHA512等要好得多。最新的算法如PBKDF2,SCRYPT或ARGON2在散列方面是领导者。但是在这种情况下使用PBKDF2是有用的,因为它是由.NET在ConfigureAwait(false)
类中实现的。到现在为止,使用该库还是很棒的,但是存在一些小问题,例如:
a。 Zetetic默认使用5000次迭代。如果您使用java.sql.SQLException: No suitable driver found for jdbc:mysql//localhost:3306/employee
at java.sql.DriverManager.getConnection(DriverManager.java:700)
at java.sql.DriverManager.getConnection(DriverManager.java:258)
at jdbcdemo.Driver.main(Driver.java:14
b。出于某些原因,Zetetic使用Rfc2898DeriveBytes
和Pbkdf2Hash256K
基于Rfc2898DeriveBytes
,因此无法自定义。
好消息!我已自定义Rfc2898DeriveBytes
以使用HMACSHA1
进行128,000次迭代,以便SQLMembershipProvider可以使用PBKDF2,到目前为止尚不可用。为此,我将Zetetic's代码与Rfc2898DeriveBytes
的实现结合在一起,如下所示:
HMACSHA512
Rfc2898DeriveBytes
创建此类后,请执行以下操作:
将以下行添加到Global.asax的Application_Start事件或项目的相应启动文件中:
using System.Security.Cryptography;
并将web.config更改为:
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;
}
}
构造此答案的参考来自:
答案 6 :(得分:0)
我附上一个显示代码的代码片段,如上面在F#中的Rawbert答案
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)
这是来自活动应用程序的工作代码