C# 数据库中存储的SecureString密码

C# 数据库中存储的SecureString密码,c#,sql-server,wpf,hash,securestring,C#,Sql Server,Wpf,Hash,Securestring,我相信我误解了SecureString的一个基本部分。我知道string是不可变的,并且在堆中可以找到密码或敏感数据作为明文 我很难理解的是,如何在需要验证数据库中散列密码的客户端应用程序中使用SecureString 以下是我的背景: 我正在使用WPF客户端应用程序 我有一个本地SQL数据库(在客户机上) 密码被散列并存储在数据库中 用户尝试登录到我的WPF应用程序 PasswordBox控件通过SecurePassword属性将密码存储在SecureString中 现在怎么办如何对Sec

我相信我误解了
SecureString
的一个基本部分。我知道
string
是不可变的,并且在堆中可以找到密码或敏感数据作为明文

我很难理解的是,如何在需要验证数据库中散列密码的客户端应用程序中使用
SecureString

以下是我的背景:

  • 我正在使用WPF客户端应用程序
  • 我有一个本地SQL数据库(在客户机上)
  • 密码被散列并存储在数据库中
  • 用户尝试登录到我的WPF应用程序
  • PasswordBox控件通过
    SecurePassword
    属性将密码存储在
    SecureString
现在怎么办如何对
SecureString
进行哈希运算而不首先将其转换回字符串?

到目前为止,我收到的所有建议都是编写扩展方法,将
SecureString
转换为
String
,对其进行散列,然后将其发送给db进行验证但这会破坏整个练习


我必须接受
SecureString
在我提到的上下文中是无用的,并使用普通的
string

SecureString表示为字节[]您可以对字节进行编码,例如使用位转换器并保存结果。 此外,SecureString是一种加密而不是散列,因为它可以被解密。(见下文)

SecureString主要用于在内存中存储敏感数据。 如果您有一个服务/网站,这并不像存储在数据库中的值那么重要。这些内容永远不能是明文,并且imo不能由您或任何管理员解密 此外,我不确定是否有其他服务器可以解密字符串,因此当您更改服务器或出现某种集群场景时,可能会出现问题

特别是对于密码,您更喜欢使用哈希算法(例如)。 这些不能被取消加密(比如数字的总和)。 在登录功能的用例中,您将加密用户输入,并将用户的哈希值与数据库中的哈希值进行比较。(详情如下) 我还建议在hashinput中添加一个动态条件,比如userid,这样两个具有相同密码的用户将拥有不同的哈希值

有了这个策略,你就不会有一个带有用户密码的risc,因此,如果数据泄露,在这一点上也不会有问题

下面是使用散列算法的简短概述

因此(如果给定securestring)首先

而不是使用SHA256对其进行散列

存储这个哈希值。根据您的编码,它可能如下所示:

AC5B208B4D35EC79FA7C14B7A31F9C80392BC58BC5B79BCFE64B044D899

在您的数据库中。 如果用户登录,则根据其输入创建哈希,并将其与数据库中的哈希进行比较。如果它们相等,则密码是正确的。 因此,您永远不需要将明文用户密码存储在任何位置

如果客户端生成哈希,则它应该绝对不应该以明文形式存在(如果不支持securestring,则文本框除外)

附言:这只是一种选择。但最重要的是不要在任何地方存储明文密码。最好不要知道它们,也不要改变它们的密码

另一种策略是使用类似的非对称加密,但这可能会变得更复杂。如果你需要帮助,我会推荐一个专门的帖子


根据您的需求和环境,大多数情况下,哈希和应该是可接受的解决方案。(但这不是一个法律建议,因为我不是律师)

就个人而言,我发现SecureString几乎没有什么用处——它们几乎没有为应用程序添加任何安全性,因为它们需要付出所有努力才能真正正确使用。在一天结束时,一个真正的专用攻击者将获得你的密码,因为它必须存储在内存中的某个地方。。。SecureStrings会让它变得更难的想法是正确的,但是如果攻击者已经在查看内存,那么我认为他们已经足够专注于通过它了。让数据库处理密码。将用户名和密码发送到存储过程,存储过程将返回身份验证是否成功。您可以处理过程中的所有加密内容。@MikeEason是否将密码作为明文发送到存储过程?从未!我想对此作出警告/补充,因为这取决于一些细节。例如,您应该建立一个安全的连接。否则,“每个人”都可以从网络流中读取密码。如果他使用的是安全字符串,则表示他希望保护自己的密码;)@尼尔斯看看我的书answer@MikeEason密码不能被加密,因为这意味着密码可以被解密。相反,它们必须用单向函数散列。然后将其存储在数据库中,并对用户提交的值进行散列并进行比较。此外,建议在散列过程中添加某种盐(只需在密码字节中添加一些常量随机字节)。这将使使用rainbow表和其他哈希攻击变得更加困难。但这也值得再发一篇完整的帖子,信息可以在网上找到。对!das上面提到的用户id:)还可以防止一些攻击,如果用户可能知道他的散列,他可能会查找具有相同散列的其他用户。没有盐,他可以用他的密码登录。啊,没错,在密码里面他没有注意到。抱歉:)我在代码中添加了一条注释,这更清楚了:)您需要先从
SecureString
直接转到
byte[]
,而不使用中间
string
步骤!
String SecureStringToString(SecureString value){
  IntPtr valuePtr = IntPtr.Zero;
  try{
    valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
    return Marshal.PtrToStringUni(valuePtr);
  }
  finally{
    Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
  }
}
  using (SHA256 hash = SHA256Managed.Create()) {
    Encoding enc = Encoding.UTF8;

    //the user id is the salt. 
    //So 2 users with same password have different hashes. 
    //For example if someone knows his own hash he can't see who has same password
    string input = userInput+userId;
    Byte[] result = hash.ComputeHash(enc.GetBytes(input));

    foreach (Byte b in result)
      Sb.Append(b.ToString("x2")); //You could also use other encodingslike BASE64 
  }