【问题标题】:Saving a SecureString保存安全字符串
【发布时间】:2017-05-25 21:15:36
【问题描述】:

我对正在开发的程序提出的功能要求之一是能够保存用户输入的凭据列表,以便可以共享它们。激发这个请求的具体用例是在一个大型企业网络上使用我们的程序,该网络由相当好的 LAN 组成,通过一个不稳定的 WAN 连接。我们的想法是,与其让我们的程序在 WAN 宕机时与它对抗,不如发送一个包含严密保护的管理员凭据的“配置”文件,在每个 LAN 中运行它并将结果压缩并通过电子邮件发送返回。

是的。

我最初的直觉是对这个请求嗤之以鼻——保存密码? 真的吗? 而且公司的网络部门肯定更希望你尝试销售他们拥有的任何 WAN 产品 - 但事实证明,我使用凭证的其中一个类可以采用 SecureString,并且,好吧,寻找可以为人们节省一些精力的方法总是好的。这让我想知道:

是否可以保存加密的 SecureString,以便我可以将敏感数据保存到文件并在其他地方打开?

你的想法是什么,堆栈溢出?

【问题讨论】:

  • 你是什么意思“在网络故障的情况下共享”?你能详细说明一下吗?

标签: .net securestring


【解决方案1】:

SecureString 中不支持保存,它旨在作为一种保护内存中托管字符串的机制,仅用于与非托管 API 交互。如果密码存储在 System.String 实例中,由于 System.String 的性质,安全性会降低。垃圾收集和实习的存在将使密码在内存中的时间超过必要的时间。此外,由于 .NET 有大量出色的调试工具,即使没有更长的生命周期,通过反射或其他 .NET API 访问字符串也会变得更加容易。

如果您要在磁盘上保存密码,您的安全性就会受到很大影响。如果有人可以物理访问机器,或管理员级别的远程访问,那么您能做的最好的事情就是让它变得更加困难,但绝不是不可能的。使用加密 API,将其存储在安全位置,配置访问权限。

除此之外,Merus,我建议您尝试改进整个系统,因为对于您所描述的用例(假设我理解它),您最好存储哈希而不是实际密码。

【讨论】:

  • 我希望 SecureString 会为我做哈希。我知道最好不要滚动我自己的哈希函数。
  • 在powershell中可以保存SecureString,任何方式使用C#?? stackoverflow.com/questions/11174425/…
【解决方案2】:

您可能想查看比较密码哈希。
您将有一个由用户名和可能其他一些常量组成的盐,然后是字符串。然后,您会将其传递给哈希算法,例如 SHA1*。
例如,

using System.Security.Cryptography;

public byte[] GetPasswordHash(string username, string password, string salt)
{
    // get salted byte[] buffer, containing username, password and some (constant) salt
    byte[] buffer;
    using (MemoryStream stream = new MemoryStream())
    using (StreamWriter writer = new StreamWriter(stream))
    {
        writer.Write(salt);
        writer.Write(username);
        writer.Write(password);
        writer.Flush();

        buffer = stream.ToArray();
    }

    // create a hash
    SHA1 sha1 = SHA1.Create();
    return sha1.ComputeHash(buffer);
}

然后,您会将GetPasswordHash(username, expectedPassword, salt) 的结果与GetPasswordHash(username, givenPassword, salt) 进行比较。
如果您使用用户名和密码实现自己的用户列表,您可能会考虑只保存哈希 (GetPasswordHash(username, givenPassword, salt)) 并与保存的哈希进行比较。

【讨论】:

  • 有时比较给定密码不是任务,而是程序本身想使用密码在其他系统中获得授权。就像邮件客户端使用密码在 SMTP 服务器中获得授权一样。在那种情况下我们能做些什么
  • @Sankaran:每个案例都有自己的解决方案。大多数现代 API 已经接受密码哈希;有些有 API 密钥,所以你甚至不需要知道密码的哈希值。要使用仅支持直接使用密码的旧 API,您别无选择,只能存储密码本身。在这种情况下,我会使用某种加密,尽管它几乎是徒劳的。
  • MD5 完全不适合作为哈希算法的选择。您需要 bcrypt 或 scrypt,或至少 sha1(甚至不建议这样做)。
  • @JoelCoehoorn:绝对正确。早在 08 年我不知道,我猜。我已经相应地更新了帖子以使用 SHA1 - 因为与 bcrypt 不同,SHA1 内置于 .Net。
【解决方案3】:

如果您的意思是保存 SecureString 的加密字节,那么这将不起作用 - SecureString 的密钥与用户和进程相关联。在不同的进程中或为不同的用户读取这些字节,并且无法解密字符串。

【讨论】:

    【解决方案4】:

    这会破坏 SecureString 的点,它保证驻留在内存中。因此,如果您将其保存到文件中,不妨将其保存为普通字符串,因为它不再“安全”。

    【讨论】:

      【解决方案5】:

      您可能想看看IsolatedStorageFileStream class。它指定了写入和存储只能由程序集访问的文件数据的方法。

      我认为你不能在上面使用 SecureString。

      【讨论】:

        【解决方案6】:

        SecureString 是不可序列化的,因此您不能只使用一些交付的序列化程序(二进制、XML 等)保存它

        您也不能只访问例如来自安全字符串对象的“密码”属性,因为没有这样的东西。 你必须使用编组和一些管道来做到这一点。 如果您想将用户凭据存储在某处,我建议您自己加密它们,因为这样可以简化以后的开发。在评估了 SecureString 方法后,我们决定自己实现一些东西,但这只是我的 2 美分。

        【讨论】:

          【解决方案7】:

          如此处所述,SecureString 不是在这种情况下使用的最佳方式。

          如果我需要 共享 用户凭据,我可能会共享 用户名hashed + salted 密码,所以你会确保安全,只分享密码表示,而不是其内容。

          简单的方法是例如使用 BCryt 库 (where there's a .NET representation of it) 并将用户名和密码表示形式托管在准备好共享的表中

          这就是你将如何使用它:

          StringBuilder sb = new StringBuilder();
          
          // run ADO.NET / Entity Framework / etc and query your DB with something like:
          "SELECT user_id, username, password FROM [TblUsers];"
          
          // loop through the results
          
          sb.AppendFormat(
                 "INSERT INTO [TblSharedUsers] SELECT '{0}','{1}','{2}'",            
                     dr["id"], dr["username"],
                     BCrypt.HashPassword(dr["password"], BCrypt.GenerateSalt(12));
                 );
          
          // then run sb.ToSTring() agains your db
          

          在客户端,您可以创建一个新的AuthenticationProvider(这样在网络不可用时使用IoCDI 可以很容易地更改)从TblSharedUsers 读取并检查用户密码喜欢

          string userPassword = Request["password"];
          
          "SELECT id, password FROM [TblSharedUsers] WHERE username = @usr;"
          
          if( BCrypt.CheckPassword(userPassword, dr["password"]) )
              // User can log in
          else
              // Credentials are invalid
          

          我希望这可以帮助遇到同样问题的人。

          【讨论】:

            猜你喜欢
            • 2013-03-02
            • 1970-01-01
            • 2018-09-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多