C# 将哈希密码存储为UTF8字符串的含义?

C# 将哈希密码存储为UTF8字符串的含义?,c#,.net,utf-8,passwords,md5,C#,.net,Utf 8,Passwords,Md5,我发现以下代码用于在将密码存储到MSSQL数据库(该列的类型为NVARCHAR)之前对密码进行哈希运算 string HashPassword(字符串密码) { var encoding=encoding.UTF8, var plainBytes=encoding.GetBytes(密码); var hashedBytes=MD5.Create().ComputeHash(纯字节); 返回encoding.GetString(hashedBytes);//这可能行得通,但实际上是一种不好的做法。

我发现以下代码用于在将密码存储到MSSQL数据库(该列的类型为NVARCHAR)之前对密码进行哈希运算

string HashPassword(字符串密码)
{
var encoding=encoding.UTF8,
var plainBytes=encoding.GetBytes(密码);
var hashedBytes=MD5.Create().ComputeHash(纯字节);

返回encoding.GetString(hashedBytes);//这可能行得通,但实际上是一种不好的做法。至少转换将依赖于本地字符集。

这可能行得通,但实际上是一种不好的做法。至少转换将依赖于本地字符集。

您正在通过减少可能编码的字符串数量来削弱安全性。任何时候,您的哈希最终都是一个invalid UTF-8序列,您将以U+FFFD作为输出字符(Unicode“替换”字符)结束。这意味着多个哈希以相同的字符串结束:

using System;
using System.Text;

class Program
{
    static void Main(string[] args)
    {
        byte[] hash1 = FillBytes(128);
        byte[] hash2 = FillBytes(129);
        string text1 = Encoding.UTF8.GetString(hash1);
        string text2 = Encoding.UTF8.GetString(hash2);
        Console.WriteLine(text1 == text2);
    }

    static byte[] FillBytes(byte data)
    {
        byte[] bytes = new byte[16];
        for (int i = 0; i < bytes.Length; i++)
        {
            bytes[i] = data;
        }
        return bytes;
    }
}
使用系统;
使用系统文本;
班级计划
{
静态void Main(字符串[]参数)
{
字节[]哈希1=填充字节(128);
字节[]哈希2=填充字节(129);
stringtext1=Encoding.UTF8.GetString(hash1);
stringtext2=Encoding.UTF8.GetString(hash2);
Console.WriteLine(text1==text2);
}
静态字节[]填充字节(字节数据)
{
字节[]字节=新字节[16];
for(int i=0;i
根据配置方式的不同,
GetString
返回的文本也可能无法正确存储在SQL Server中。(如果该字段设置为可以以Unicode格式存储任何内容,则该部分没有问题。)如果它正在丢失数据,那就更糟了——存储的正确哈希值与计算的正确哈希值不匹配,因此键入正确密码的人仍将被拒绝访问。正如我所说,这可能不是问题——但您没有给我们足够的信息来确定,因此至少值得考虑。如果您使用Base64或hex,两者都以ASCII数据结束

使用MD5对密码进行散列是一个不好的开始-通过有损文本转换进一步削弱密码更糟糕。它使攻击者更容易找到仍然以相同文本结尾的错误密码

我建议:

  • 您使用更安全的哈希方法(例如bcrypt或PBKDF2)-有关更多详细信息,请参阅(有关更多信息,请阅读安全书籍)
  • 要存储散列,可以使用blob(直接存储字节)或转换为base64或hex以保留完整信息

通过减少可能要编码的字符串的数量,您正在削弱安全性。任何时候,当您的哈希最终成为无效的UTF-8序列时,您将以U+FFFD作为输出字符(Unicode“替换”字符)。这意味着多个哈希以相同的字符串结束:

using System;
using System.Text;

class Program
{
    static void Main(string[] args)
    {
        byte[] hash1 = FillBytes(128);
        byte[] hash2 = FillBytes(129);
        string text1 = Encoding.UTF8.GetString(hash1);
        string text2 = Encoding.UTF8.GetString(hash2);
        Console.WriteLine(text1 == text2);
    }

    static byte[] FillBytes(byte data)
    {
        byte[] bytes = new byte[16];
        for (int i = 0; i < bytes.Length; i++)
        {
            bytes[i] = data;
        }
        return bytes;
    }
}
使用系统;
使用系统文本;
班级计划
{
静态void Main(字符串[]参数)
{
字节[]哈希1=填充字节(128);
字节[]哈希2=填充字节(129);
stringtext1=Encoding.UTF8.GetString(hash1);
stringtext2=Encoding.UTF8.GetString(hash2);
Console.WriteLine(text1==text2);
}
静态字节[]填充字节(字节数据)
{
字节[]字节=新字节[16];
for(int i=0;i
根据配置方式的不同,
GetString
返回的文本也可能无法正确存储在SQL Server中。(如果该字段设置为可以以Unicode格式存储任何内容,则该部分没有问题。)如果它正在丢失数据,那就更糟了——存储的正确哈希值与计算的正确哈希值不匹配,因此键入正确密码的人仍将被拒绝访问。正如我所说,这可能不是问题——但您没有给我们足够的信息来确定,因此至少值得考虑。如果您使用Base64或hex,两者都以ASCII数据结束

使用MD5对密码进行散列是一个不好的开始-通过有损文本转换进一步削弱密码更糟糕。它使攻击者更容易找到仍然以相同文本结尾的错误密码

我建议:

  • 您使用更安全的哈希方法(例如bcrypt或PBKDF2)-有关更多详细信息,请参阅(有关更多信息,请阅读安全书籍)
  • 要存储散列,可以使用blob(直接存储字节)或转换为base64或hex以保留完整信息

原来的“哈希到文本”转换被指定为UTF-8;它不依赖于“本地字符集”。该文本是否能正确存储在SQL Server中将取决于数据库的配置方式,但这是另一回事。如果这是您的意思,您的答案肯定会更清楚。看起来我看错了事件的真实顺序。我(不知道为什么?)认为encoding.GetString(hashBytes)将ANSI字符串编码为utf-8。没错,问题在于尝试utf-8解码(几乎)随机字节序列,这很可能不是有效的utf8序列。好的,原始的“哈希到文本”转换指定为utf-8;这不依赖于“本地字符集”。该文本是否能正确存储在SQL Server中将取决于数据库的配置方式,但这是另一回事。如果这是您的意思,您的答案肯定会更清楚。看起来我看错了事件的真实顺序。我(不知道为什么?)认为encoding.GetString(hashBytes)将ANSI字符串编码为utf-8。没错,问题在于尝试对(几乎)随机字节序列进行utf-8解码,而这很可能不是有效的utf8序列。