如何在我的salt&;散列码
本文关键字:amp 我的 salt | 更新日期: 2023-09-27 18:22:26
我对密码学完全陌生,但正在学习。我从网上的研究中拼凑出了许多不同的建议,并为处理哈希、salt、密钥拉伸和相关数据的比较/转换创建了自己的类。
在研究了用于密码学的内置.NET库之后,我发现我所拥有的仍然只有SHA-1。但我得出的结论是,这还不错,因为我使用了哈希过程的多次迭代。这是正确的吗?
但如果我想从更健壮的SHA-512开始,我该如何在下面的代码中实现它呢?提前谢谢。
using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
public class CryptoSaltAndHash
{
private string strHash;
private string strSalt;
public const int SaltSizeInBytes = 128;
public const int HashSizeInBytes = 1024;
public const int Iterations = 3000;
public string Hash { get { return strHash; } }
public string Salt { get { return strSalt; } }
public CryptoSaltAndHash(SecureString ThisPassword)
{
byte[] bytesSalt = new byte[SaltSizeInBytes];
using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
{
crypto.GetBytes(bytesSalt);
}
strSalt = Convert.ToBase64String(bytesSalt);
strHash = ComputeHash(strSalt, ThisPassword);
}
public static string ComputeHash(string ThisSalt, SecureString ThisPassword)
{
byte[] bytesSalt = Convert.FromBase64String(ThisSalt);
Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(
convertSecureStringToString(ThisPassword), bytesSalt, Iterations);
using (pbkdf2)
{
return Convert.ToBase64String(pbkdf2.GetBytes(HashSizeInBytes));
}
}
public static bool Verify(string ThisSalt, string ThisHash, SecureString ThisPassword)
{
if (slowEquals(getBytes(ThisHash), getBytes(ComputeHash(ThisSalt, ThisPassword))))
{
return true;
}
return false;
}
private static string convertSecureStringToString(SecureString MySecureString)
{
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.SecureStringToGlobalAllocUnicode(MySecureString);
return Marshal.PtrToStringUni(ptr);
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(ptr);
}
}
private static bool slowEquals(byte[] A, byte[] B)
{
int intDiff = A.Length ^ B.Length;
for (int i = 0; i < A.Length && i < B.Length; i++)
{
intDiff |= A[i] ^ B[i];
}
return intDiff == 0;
}
private static byte[] getBytes(string MyString)
{
byte[] b = new byte[MyString.Length * sizeof(char)];
System.Buffer.BlockCopy(MyString.ToCharArray(), 0, b, 0, b.Length);
return b;
}
}
注意:我参考了许多来自https://crackstation.net/hashing-security.htm.slowEquals比较方法是通过防止分支来规范执行时间。SecureString的用途是在这个类和我的web应用程序中的其他类和页面之间传递加密形式的密码。虽然这个网站将通过HTTPS,但在合理的范围内,尽最大努力确保事情尽可能安全总是很好的。
在我的代码中,我将密钥字符串设置为128字节(尽管有时会变大,这很好),将哈希大小设置为1KB,迭代次数设置为3000。它比典型的64字节salt、512字节hash和1000或2000次迭代稍大,但登录速度和应用程序性能的优先级极低。
想法?
- 3000次迭代是相当低的。即使是10000也很低。但是,您需要权衡额外迭代的安全性增益与攻击者通过经常尝试登录来拒绝您的服务器的风险,这会为每次尝试触发昂贵的哈希
- 在大于128位/16字节的salt中没有任何点。盐应该是独一无二的
-
哈希大小大于本机大小(SHA-1为20字节)会降低防御者的性能,但不会降低攻击者的性能。由于这意味着您可以承担更少的迭代,因此实际上会削弱安全性。
例如,以与具有3000次迭代的1024字节哈希相同的成本,您可以负担具有156000次迭代的20字节哈希,这是破解成本的52倍。
-
要使用SHA-2,您需要一个完全不同的PBKDF2实现,.net中包含的实现被硬编码为使用SHA-1。
如果你想使用第三方库,我宁愿使用bcrypt库,因为它对基于GPU的攻击者更强大。
-
API很难使用,因为您将盐管理推送到调用方,而不是在
Create
/Verify
函数中处理它。 -
使用
SecureString
然后将其转换为String
是愚蠢的。这从一开始就抵消了使用SecureString
的全部意义。就我个人而言,我不会在典型的应用程序中使用
SecureString
。只有将其与广泛的全栈安全审查相结合,才能检查密码是否从未存储在String
中,并且在不再需要密码时始终从可变存储中删除密码,这才是值得的。 -
我不会在实例变量中存储密码/盐。只需将它们放在相关功能的本地即可。我只会将配置存储在实例变量中(例如迭代次数)。
-
虽然SHA-1在密码上被削弱,但攻击会产生冲突。由于密码哈希冲突无关紧要,您关心的是第一次映像前攻击。SHA-1在这方面仍然相当强大。
SHA-512的主要优点并不是它在加密方面更强(尽管它确实更强),而是64位算术比防御者花费更多,因为防御者可能会使用64位Intel CPU,它提供快速的64位算术。
如果有人在搜索中遇到这个问题,现在微软提供了Microsoft.AspNetCore.Cryptography.KeyDerivation NuGet包,它允许将PBKDF2与SHA-256和SHA-512哈希函数一起使用。文档可在learn.microsoft.com上获得。
回答问题:从"SecurityDriven.NET"一书下载免费的代码示例。查找采用HMAC工厂的PBKDF2类HMACSHA512工厂可用。
既然你是密码学的新手,我也强烈建议你读这本书(例如,为了充分理解CodesInChaos提出的观点)。
对于那些通过搜索得出的结果,答案是Rfc2898DeriveBytes
构造函数将哈希算法作为参数,HashAlgorithmName.SHA512
是一个受支持的选项。所以,这是有效的:
var pbkdf2 = new Rfc2898DeriveBytes(passwordBytes, bytesSalt, 1000000, HashAlgorithmName.SHA512);