铁的事实是,在桌面应用程序中存储密码,100% 安全是根本不可能的。但是,您可以接近 100%。
关于您的原始方法,PasswordVault 使用 Windows 内置的Credential Locker服务来安全地存储数据。Credential Locker 绑定到用户的个人资料。因此,通过 PasswordVault 存储您的数据本质上等同于保护数据的主密码方法,我将在下面详细讨论。唯一不同的是,在这种情况下,主密码是用户的凭据。这允许在用户会话期间运行的应用程序访问数据。
注意:为了清楚起见,我严格地说是以允许您访问纯文本的方式存储它。也就是说,将其存储在任何类型的加密数据库中,或者自己加密并将密文存储在某个地方。这种功能在密码管理器等程序中是必需的,但在只需要某种身份验证的程序中则不需要。如果这不是必需的,那么我强烈建议对密码进行哈希处理,最好按照zaph在此答案中列出的说明进行。( Thomas Pornin的这篇优秀文章中提供了更多信息)。
如果这是必要的,事情会变得有点复杂:如果你想阻止其他程序(或我想的用户)能够查看明文密码,那么你唯一真正的选择就是加密它。在 PasswordVault 中存储密文是可选的,因为如果您使用良好的加密,您唯一的弱点就是有人发现了您的密钥。因此密文本身可以存储在任何地方。这将我们带到了关键本身。
根据您实际尝试为每个程序实例存储的密码数量,您可能根本不必担心生成和安全存储密钥。如果您想存储多个密码,那么您可以简单地要求用户输入一个主密码,对其执行一些加盐和散列,然后将结果用作所有其他密码的加密密钥。当需要解密时,请用户再次输入。如果您要存储多个密码,那么我强烈建议您采用这种方法。这是最安全的方法。然而,对于我的帖子的其余部分,我将假设这不是一个可行的选择。
首先,我敦促您不要为每个安装使用相同的密钥。根据安全生成的随机数据,为程序的每个实例创建一个新实例。抵制“避免存储密钥”的诱惑,每次需要时根据系统信息动态生成密钥。string superSecretKey = "12345";
这与硬编码到您的程序中一样安全。攻击者很快就能弄清楚这个过程。
现在,存储它是真正棘手的部分。信息安全的一般规则如下:
一旦你有物理访问权,没有什么是安全的
所以,理想情况下,没有人会。将加密密钥存储在适当安全的远程服务器上可以最大限度地减少攻击者恢复它的机会。关于服务器端安全的整本书都写过,所以我不会在这里讨论这个。
另一个不错的选择是使用 HSM(硬件安全模块)。这些漂亮的小设备是为这项工作而设计的。访问存储在 HSM 中的密钥几乎是不可能的。但是,此选项仅在您确定每个用户的计算机都具有其中之一的情况下才可行,例如在企业环境中。
.Net 通过配置系统提供了各种解决方案。您可以将密钥存储在app.config
. 这通常用于保护连接字符串。有很多关于如何做到这一点的资源。我推荐这篇精彩的博客文章,它将告诉你大部分你需要知道的东西。
我之前说过不要简单地动态生成密钥的原因是,就像将它作为变量存储在代码中一样,你完全依赖于混淆来保证它的安全。这种方法的问题是它通常不会。但是,有时您别无选择。输入白盒密码学。
白盒密码学本质上是将混淆技术发挥到了极致。它意味着即使在白盒场景中也是有效的,攻击者既可以访问也可以修改字节码。它是通过默默无闻实现安全的缩影。与单纯的不断隐藏(信息安全代表该string superSecretKey
方法)或在需要时生成密钥相反,白盒密码术本质上依赖于动态生成密码本身。
整篇论文都写在上面,很难写出一个正确的实现,而且你的里程可能会有所不同。仅当您真的很想尽可能安全地执行此操作时,才应考虑这 一点。
然而,混淆仍然是混淆。它真正能做的就是减慢攻击者的速度。我必须提供的最终解决方案可能看起来倒退,但它确实有效:不要以数字方式隐藏加密密钥。物理隐藏它。让用户在需要加密时插入 USB 驱动器,(安全地)生成随机密钥,然后将其写入 USB 驱动器。然后,每当需要解密时,用户只需将驱动器放回原处,您的程序就会从中读取密钥。
这有点类似于主密码方法,因为它让用户来保证密钥的安全。但是,它有一些显着的优点。例如,这种方法允许使用大量加密密钥。仅可放入 1 兆字节文件的密钥可能需要数十亿年才能通过暴力攻击破解。另外,如果密钥被发现,用户只能怪自己。
总之,看看您是否可以避免存储加密密钥。如果不能,请不惜一切代价避免将其存储在本地。否则,您唯一的选择就是让黑客尽可能难以弄清楚。无论您如何选择这样做,请确保每个密钥都是不同的,因此即使攻击者确实找到了一个,其他用户的密钥也是安全的。