加密和解密密码的最佳实践?(C#/.NET)

21

我需要将密码存储和加密在一个(最好是文本)文件中,并且以后需要能够解密。该密码用于另一个我使用的服务,并需要明文(通过SSL)发送到该服务中。这不是我能改变的事情。在这方面有什么最佳实践?如何在一定程度上保护密码免受恶意用户的攻击?

我的平台是WinForms,使用C#/.NET 3.5。

谢谢。


3
标题应更改为“加密密码的最佳实践”,因为解密密码显然不是最佳实践。 - devlord
1
不应该这样做;那将完全是另一个问题。 - Eyvind
1
最佳的密码解密实践是:不要这样做。 - saluce
9个回答

12

我假设你想加密密码,因为它将保存在用户的机器上,他们可能会找到并使用它?如果是这样,无论你做什么都基本上束手无策,因为它在用户领域内,他们将能够获取它并解密密码(请记住,使用Reflector和它的克隆版本对大多数人来说并不困难)并得到它。简而言之,你所做的只是混淆密码,而不是保护它。

我建议的是将其移出用户的控制范围。例如,建立一个与客户端通信并在请求时安全返回密码的网络服务。这还使您有办法验证合法用户,并且如果需要在未来更改密码,也可以实现。


+1,但我们应该如何保护密码返回的 Web 服务的访问安全? - Matthew Murdoch
@Matthew 任何标准的服务认证系统都可以很好地工作。 - Robert MacLean
@Robert:您能详细说明一下吗?如果用户自己可以调用服务,那么安全点有点无意义。我在这里没有理解到什么?谢谢。 - Eyvind
1
例如,@Eyvind 给所有用户一个密钥(用户名/密码、唯一 ID 等),并要求在调用获取密码的方法时提供该密钥,例如:public string GetPassword(string username, string password)客户端将其密钥放入其应用程序中进行身份验证,因此您知道可以“信任”他们。这也允许您在将来需要时阻止用户。有很多关于 WCF/Web 服务身份验证的内容:http://stackoverflow.com/search?q=web+service+wcf+authentication - Robert MacLean
1
与其将密码返回给客户端,然后由客户端直接连接第三方服务,您可以考虑运行一个经过身份验证的 Web 服务,代理所有客户端请求到真实服务;您的第三方服务密码永远不会发送给客户端。这对您来说更加昂贵,并且增加了一些调用开销,但提供了最佳安全性。这取决于保护密码的价值。 - erickson

7

为什么需要解密密码?通常存储并比较的是密码的盐哈希值。如果加密/解密密码,则可以再次以明文形式获取密码,这是很危险的。哈希值应该被加盐以避免重复哈希,如果一些用户使用相同的密码,则可以使用用户名作为盐。

HashAlgorithm hash = new SHA256Managed();
string password = "12345";
string salt = "UserName";

// compute hash of the password prefixing password with the salt
byte[] plainTextBytes = Encoding.UTF8.GetBytes(salt + password);
byte[] hashBytes = hash.ComputeHash(plainTextBytes);

string hashValue = Convert.ToBase64String(hashBytes);

你可以计算密码的盐散列并将其存储在文件中。在身份验证期间,您再次从用户输入计算哈希值,并将此哈希与存储的密码哈希进行比较。 由于从哈希值获取明文应该非常困难(它永远不是不可能,只是时间问题),因此密码受到了防止以明文形式再次读取的保护。
提示:永远不要未加密地存储或发送密码。如果您获得新密码,请尽快加密!

1
如题所述,我必须对其进行解密,因为该服务仅接受未加密形式的密码。 - Eyvind
如果无法避免,我会使用AES,但请注意任何加密密钥都可以从汇编中读取,因此并不是真正安全的。 - Enyra
你好,SHA256加密是否足够安全用于程序存储明文密码文件?我正在寻找密码存储程序的描述方法,但不确定应该使用哪种方法。 - HelpNeeder
很难说这是否足够,因为我不知道你的应用程序。这取决于要保护的数据对密码的关键性以及可以通过哪些渠道访问密码。基本上,密钥越长,安全性就越高。在您的情况下,我假设您只是试图防止普通用户读取明文密码,在只有少数人可以访问它的环境中(否则您不会将其存储在密码中)。对于这种情况,这已经足够了。 - Enyra

6

System.Security.Cryptography.ProtectedData 在 System.Security 组件中使用一些 Windows API 以密码加密数据,仅它自己知道。

其中一个可能的应用是拥有一个实际执行需要密码的操作的 Windows 服务。用户交互的应用程序通过远程调用或 WCF 调用该服务。只要服务使用 DataProtectionScope.CurrentUser 并且服务用户与登录的用户不同,密码应该相对安全。

当然,这假设用户作为受限制的用户运行,无法修改服务或以服务用户身份运行程序。


2
因为您使用的是WinForms和.Net,所以即使混淆,您的代码也会以MSIL形式可见,因此您的解密代码也会可见。
您试图隐藏密码的对象是谁?应用程序的用户不应该知道密码吗?
我认为您需要进行一些用户验证,并且我会倾向于将解密密钥放在单独的数据库中,并提供一些其他机制来获取该密钥,这应该需要身份验证。这样,您就可以将解密代码从WinForms应用程序中移出。
我还建议设置一个单独的服务,定期更改加密解密密钥并更新数据库中的所有密码。

1

我刚刚实现了一个类似的东西来存储用户提供的密码。我将加密结果转换为base64编码的字符串,以便可以轻松地将其存储在我的应用程序用户设置中。

从你的问题来看,似乎你的恶意用户实际上正在使用你的应用程序,所以这只会提供混淆。虽然通过使用Reflector不会揭示任何密钥,但明文将在调试器中可见。

static byte[] entropy = { 65, 34, 87, 33 };

public string Password
{
    get
    {
        if (this.EncryptedPassword == string.Empty)
        {
            return string.Empty;
        }

        var encrypted = Convert.FromBase64String(this.EncryptedPassword);
        var data = ProtectedData.Unprotect(encrypted, entropy, DataProtectionScope.CurrentUser);
        var password = Encoding.UTF8.GetString(data);
        return password;
    }
    set
    {
        if (value == string.Empty)
        {
            this.EncryptedPassword = string.Empty;
            return;
        }

        var data = Encoding.UTF8.GetBytes(value);
        var encrypted = ProtectedData.Protect(data, entropy, DataProtectionScope.CurrentUser);
        var stored = Convert.ToBase64String(encrypted);
        this.EncryptedPassword = stored;
    }
}

1

如果你必须将其存储在文本文件中,则使用AES加密。

AES在c#中更为常被称为Rijndael。

http://www.obviex.com/samples/Encryption.aspx

更好的位置应该是注册表,因为它可以保护其他用户无法访问。

但是,将其存储在任何用户可能能够访问的地方仍然存在危险。一位相当不错的开发人员可以使用反编译工具加载您的应用程序并找到您的密钥。

或者,还有其他人建议使用 System.Security.Cryptography.ProtectedData。

在计算机上做得最好的事情是创建一个证书,并使用已加载和锁定在计算机密钥库中的证书进行加密/解密。(仍然需要处理代码中的证书密码)


1
更好的位置应该是注册表,因为它能保护其他使用该机器的用户无法访问它。将数据存储在注册表中与存储在文本文件中不会影响其他用户对该机器的访问。 - Joe
@Joe,HKEY_CURRENT_USER对于机器上的其他用户是不可访问的。他们有自己的HKCU。当然,管理员除外。http://en.wikipedia.org/wiki/Windows_registry 还有这个:http://blogs.msdn.com/bclteam/archive/2006/01/06/509867.aspx - Chad Grant
但你说得对,我并不是爱上了自己的答案,而是在有限的信息下给他提供了一些选择。个人而言,我喜欢使用webservice/aspx来代理用户的密码,但这样他就需要获取SSL证书等等。 - Chad Grant

0

不要将密码作为代码的一部分存储。除了反编译和依赖于安全性通过混淆之外,如果您更改密码,则需要重新编译和重新分发应用程序。

将密码存储为Web服务或应用程序可以访问的数据库中。毕竟,您正在通过网络与服务通信,因此您将保持连接。


0

其中最重要的事情之一是文件权限。即使内容已加密,您也需要确保只有需要访问该文件的进程才能读取它。


0

由于您必须以未加密的形式通过网络发送密码,因此无法百分之百地保护它。

如果您需要本地存储,则AES已足够好。关于反汇编、网络嗅探等方面的讨论并不是特别好的反驳观点,因为任何程序都可以做到这一点(当然,ASM比CIL更难,但这只是一个小问题)。

这种密码保护足以防止随意拾取,但无法防止专业人员的解码。


就像他所说的那样,它是通过SSL加密的。 - Chad Grant
密码是我使用的另一个服务的密码,需要以明文形式(通过SSL)发送到那个服务。 - majkinetor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接