7

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

是的。

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

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

你有什么想法,堆栈溢出?

4

7 回答 7

5

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

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

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

于 2008-09-29T07:06:37.947 回答
4

您可能想查看比较密码哈希。
您将有一个由用户名和可能其他一些常量组成的盐,然后是字符串。然后,您会将其传递给哈希算法,例如 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)to的结果GetPasswordHash(username, givenPassword, salt)
如果您使用用户名和密码实现自己的用户列表,您可能会考虑只保存哈希 ( GetPasswordHash(username, givenPassword, salt)) 并与保存的哈希进行比较。

于 2008-11-25T15:23:05.977 回答
3

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

于 2008-09-29T06:45:07.423 回答
3

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

于 2008-09-29T06:47:12.347 回答
1

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

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

于 2008-09-29T06:46:37.217 回答
1

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

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

于 2008-09-29T06:47:12.143 回答
0

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

如果我需要共享用户凭据,我可能会共享用户名hashed + salted 密码,这样你就安全了,只共享密码的表示,而不是它的内容。

简单的方法是例如使用BCryt 库其中有一个 .NET 表示形式)并将用户名和密码表示形式简单地托管在准备好共享的表中

这是你将如何使用它:

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

在客户端,您可以创建一个新的(因此当网络不可用时使用IoCDIAuthenticationProvider很容易更改)读取并检查用户密码,例如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

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

于 2011-09-07T06:53:50.660 回答