从桌面应用程序使用时的 PasswordVault 安全性

PasswordVault security when used from Desktop app

我想使用 Windows.Security.Credentials.PasswordVault in my desktop app (WPF-based) to securely store a user's password. I managed to access this Windows 10 API using this MSDN article

我做了一些实验,似乎从一个桌面应用程序(不是本机 UWP 应用程序)写入 PasswordVault 的任何数据都可以从任何其他桌面应用程序读取。即使使用 Desktop Bridge 技术打包我的桌面应用程序并因此拥有包标识也无法修复此漏洞。

有什么想法可以解决这个问题并能够安全地存储应用程序的数据以免受其他应用程序的影响吗?

更新: PasswordVault 似乎没有比 DPAPI 增加额外的安全性。此案以否定结果结案。

(这是我对你的理解post)

当使用这些 API 时,没有真正的方法来阻止桌面应用程序之间的数据访问 http://www.hanselman.com/blog/SavingAndRetrievingBrowserAndOtherPasswords.aspx 详细说明了这一点。您可能只想解密您的信息。

内存访问限制很困难,用户执行的代码总是可以被用户检索,所以很难限制这个。

您是否考虑过使用 Windows 数据保护 API: https://msdn.microsoft.com/en-us/library/ms995355.aspx

直接从源头抓取 DPAPI 是一项易于使用的服务,将使必须为敏感应用程序数据(例如密码和私钥)提供保护的开发人员受益

WDPAPI 使用操作系统生成的密钥和 Triple DES 来 encrypt/decrypt 您的数据。这意味着您的应用程序不必生成这些密钥,这总是很好。

您也可以使用 Rfc2898DeriveBytes class,它使用伪随机数生成器来解密您的密码。它比大多数解密器更安全,因为没有实用的方法可以从结果返回到密码。这仅在验证输入密码而不是再次取回密码时真正有用。我自己从来没有实际使用过这个所以我无法帮助你。

https://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes(v=vs.110).aspx

另请参阅此 post,它提供了比我更好的解释方式。 How to securely save username/password (local)?

如果我在某些方面误解了问题,请告诉我,我会尝试更新答案。

请注意,modern/metro 应用程序没有此问题,尽管它们仍然可以通过其他方式访问。

唯一的选择是使用存储在代码中某处的您自己的私钥来加密密码。 (有人可以很容易地反汇编你的代码并获得密钥)然后将加密的密码存储在 PasswordVault 中,但是你唯一的安全是任何应用程序都无法访问密码。

这是双重安全性,如果机器受到威胁,攻击者可以访问 PasswordVault 但不能访问您的密码,因为他们将需要一个私钥来解密密码,并且该私钥将隐藏在您的代码中的某处。

为了更安全,如果您将私钥留在您的服务器上并公开一个 API 来加密和解密密码,然后再将其存储在 Vault 中,将使其最安全。我认为这就是人们转向 OAuth(将 OAuth 令牌存储在 PasswordVault 中)等而不是将密码存储在保险库中的原因。

理想情况下,我建议不要存储密码,而是从服务器获取一些令牌并保存并使用该令牌进行身份验证。并将该令牌存储在 PasswordVault 中。

残酷的事实是,在桌面应用程序中 100% 安全地存储密码根本不可能。 但是,您可以接近 100%。

关于您的原始方法,PasswordVault 使用内置于windows 中的Credential Locker 服务来安全地存储数据。 Credential Locker 绑定到用户的个人资料。因此,通过 PasswordVault 存储数据本质上等同于 主密码 保护数据的方法,我将在后面详细讨论。唯一的区别是,在这种情况下,主密码是用户的凭据。这允许应用程序 运行 在用户会话期间访问数据。

Note: To be clear, I'm strictly talking about storing it in a way that allows you access to the plain text. That is to say, storing it in an encrypted database of any sort, or encrypting it yourself and storing the ciphertext somewhere. This kind of functionality is necessary in programs like password managers, but not in programs that just require some sort of authentication. If this is not a necessity then I strongly recommend hashing the password, ideally per the instructions laid out in answer by zaph. (Some more information in this excellent post by Thomas Pornin).


如果必要的,事情会变得有点复杂:如果你想阻止其他程序(或者我想是用户)能够查看明文密码,那么你唯一真正的选择就是加密它。将密文存储在 PasswordVault 中是可选的,因为如果您使用良好的加密,您唯一的弱点就是有人发现了您的密钥。因此密文本身可以存储在任何地方。这就把我们带到了关键本身。

根据您实际尝试为每个程序实例存储的密码数量,您可能根本不必担心生成和安全存储密钥。如果你想存储多个密码,那么你可以简单地要求用户输入一个 主密码 ,对其进行一些加盐和散列,并将结果用作所有其他密码的加密密钥密码。到了解密的时候,再要求用户输入。如果您要存储多个密码,那么我强烈建议您采用这种方法。这是最安全的方法。然而,对于我 post 的其余部分,我将假设这不是一个可行的选择。

首先,我强烈建议您不要每次安装都使用相同的密钥。根据安全生成的随机数据,为程序的每个实例创建一个新实例。抵制 "avoid having to store the key" 的诱惑,让它在每次需要时根据系统信息即时生成。这与将 string superSecretKey = "12345"; 硬编码到您的程序中一样安全。攻击者不需要很长时间就能弄清楚这个过程。


现在,存储它才是真正棘手的部分。信息安全的一般规则如下:

Nothing is secure once you have physical access

所以,理想情况下,没有人会这样做。将加密密钥存储在适当保护的远程服务器上可以最大限度地减少它被攻击者恢复的机会。关于服务器端安全的书籍已经写了很多,所以我不会在这里讨论这个。

另一个不错的选择是使用 HSM (Hardware Security Module)。这些漂亮的小设备专为这项工作而打造。访问存储在 HSM 中的密钥几乎是不可能的。但是,只有当您确定每个用户的计算机都有其中之一时(例如在企业环境中),此选项才可行。

.Net 通过配置系统提供了各种解决方案。您可以将密钥存储在 app.config 的加密部分。这通常用于保护连接字符串。有很多关于如何执行此操作的资源。我推荐this很棒的博客post,它会告诉你大部分你需要知道的东西。


我之前说不要简单地即时生成密钥的原因是,就像将它存储为代码中的变量一样,您完全依赖 混淆 来保持安全。这种方法的问题在于它 通常不会 。但是,有时您别无选择。输入 White Box cryptography.

白盒密码学本质上是一种极端的混淆。它意味着即使在白盒场景中也是有效的,在这种情况下,攻击者既可以访问也可以修改字节码。它是通过默默无闻实现安全的缩影。与仅仅不断隐藏(信息安全代表 string superSecretKey 方法)或在需要时生成密钥相反,白盒密码学本质上依赖于动态生成 密码本身 .

整个 papers 都写在上面了,很难写出正确的实现,你的里程可能会有所不同。只有当你真的 真的 真的 想要尽可能安全地这样做时,你才应该考虑这个。


混淆仍然是混淆。它真正能做的就是减慢攻击者的速度。我必须提供的最终解决方案可能看起来倒退,但它确实有效:不要以数字方式隐藏加密密钥。 物理地 隐藏它。让用户在加密时插入一个 USB 驱动器,(安全地)生成一个随机密钥,然后将其写入 USB 驱动器。然后,每当需要解密时,用户只需将驱动器放回原处,您的程序就会从中读取密钥。

这有点类似于主密码方法,因为它将由用户来保证密钥的安全。但是,它有一些显着的优点。例如,此方法允许 大量 加密密钥。仅可容纳 1 兆字节文件的密钥可能需要 十亿 年才能通过暴力攻击破解。另外,如果密钥被发现,用户只能怪自己。


总结,看看您是否可以避免存储加密密钥。如果不能,请不惜一切代价避免将其存储在本地。否则,您唯一的选择就是让黑客尽可能难以破解。无论您如何选择,请确保每个密钥都不同,这样即使攻击者确实找到了一个,其他用户的密钥也是安全的。

通过各种加密和存储策略,始终可以提高安全性。使事情变得更难只会使数据检索时间更长,而绝非不可能。因此,您需要考虑执行成本 x 时间(人和机器)和开发成本 x 时间方面的最合适的保护级别。

如果我严格考虑你的要求,我会简单地添加一层(class,接口)来加密你的密码。最好使用非对称加密(而不是 RSA)。假设其他软件没有访问您的程序数据(程序、文件或进程),这就足够了。您可以使用 SSH.NET (https://github.com/sshnet/SSH.NET) 来快速实现此目的。

如果您想提高安全性并针对二进制逆向工程(包括私钥检索)提供一定程度的保护,我推荐小型(进程受限)加密 VM(如 Docker , https://blogs.msdn.microsoft.com/mvpawardprogram/2015/12/15/getting-started-with-net-and-docker/) based solution such as Denuvo (https://www.denuvo.com/).每个客户和基于机器的加密都是唯一的。你必须将你的 c# 程序封装到一个 c/c++ 程序(它就像一个容器),它将完成所有内存中的加密-解密。

您可以实施自己的策略,具体取决于您需要的投资类型和保证。

如果你的程序是后端程序,你可以选择最好的策略(我唯一真正推荐的策略)是将私钥存储在客户端,public 密钥存储在后端并有本地解密,所有传输的密码将因此被加密。我想指出,密码和密钥实际上是实现同一目标的不同策略:在不知道该人身份的情况下检查程序是否与正确的人交谈;我的意思是:与其存储密码,不如直接存储 public 个密钥。

重新审视这个很有帮助的问题并添加一些可能有用的附加信息。

我的任务是扩展一个 Win32 应用程序,该应用程序使用密码通过具有“保存密码”功能的在线服务进行身份验证。想法是使用 Windows Hello (UserConsentVerifier) 来保护密码。我的印象是 Windows 肯定有类似于 macOS 钥匙串的东西。

如果您使用 Windows 凭据管理器 API(CredReadACredWriteA),另一个应用程序可以简单地枚举凭据,如果它知道要查找的内容(目标名称), 它将能够读取凭据。

我还探索了使用 DPAPI,您负责自己存储加密的 blob,通常是在文件中。同样,似乎没有办法(混淆除外)阻止另一个应用程序查找和读取该文件。向 CryptProtectDataCryptUnprotectData 提供额外的熵再次提出了将熵存储在哪里的问题(通常我假设它会被硬编码并且可能在应用程序中被混淆:这是默默无闻的安全性)。

事实证明,DPAPI(CryptProtectDataCryptUnprotectData)和 Windows Credential Manager API(CredReadCredWrite)都无法阻止另一个同一用户下的应用程序 运行ning 无法读取机密。

我实际上正在寻找的是类似 macOS 钥匙串的东西,它允许应用程序存储秘密,在这些秘密上定义 ACL,在访问秘密时强制执行生物识别身份验证,并且关键的是,防止其他应用程序读取秘密。

事实证明,Windows 有一个 PasswordVault 声称可以将应用程序彼此隔离,但它仅适用于 UWP 应用程序:

Represents a Credential Locker of credentials. The contents of the locker are specific to the app or service. Apps and services don't have access to credentials associated with other apps or services.

Win32 桌面应用程序有没有办法访问此功能?我意识到,如果可以让用户安装 运行 一个随机应用程序,该应用程序可能会模仿原始应用程序,只是提示用户输入密码,但仍然有点令人失望的是没有默认应用级分离。