21

我想在我的桌面应用程序(基于 WPF)中使用Windows.Security.Credentials.PasswordVault来安全地存储用户的密码。我设法使用这篇MSDN 文章访问了这个 Windows 10 API 。

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

任何想法如何解决这个问题并能够从其他应用程序安全地存储应用程序的数据?

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

4

5 回答 5

7

(这是我对你的帖子的理解)

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

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

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

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

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

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

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

另请参阅这篇文章,它提供了比我更好的解释。 如何安全地保存用户名/密码(本地)?

如果我以某种方式误解了这个问题,请告诉我,我会尝试更新答案。

请注意,现代/地铁应用程序没有这个问题,尽管它们仍然可以通过其他方式访问。

于 2017-08-14T13:17:15.633 回答
5

铁的事实是,在桌面应用程序中存储密码,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 兆字节文件的密钥可能需要数十亿年才能通过暴力攻击破解。另外,如果密钥被发现,用户只能怪自己。


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

于 2017-08-18T10:46:09.287 回答
2

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

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

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

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

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

于 2017-08-20T14:35:14.370 回答
2

唯一的替代方法是使用存储在代码中某处的您自己的私钥来加密密码。(有人可以轻松地反汇编您的代码并获取密钥)然后将加密密码存储在 PasswordVault 中,但是您拥有的唯一安全性是任何应用程序都无法访问密码。

这是双重安全性,万一机器受到攻击,攻击者可以访问 PasswordVault 但不能访问您的密码,因为他们需要另外一个私钥来解密密码,而这将隐藏在您的代码中的某个位置。

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

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

于 2017-08-18T07:31:38.350 回答
0

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

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

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

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

事实证明,DPAPI ( CryptProtectData, CryptUnprotectData) 和 Windows 凭据管理器 API ( CredRead, CredWrite) 都不能阻止在同一用户下运行的另一个应用程序读取机密。

我真正在寻找的是类似于 macOS 钥匙串的东西,它允许应用程序存储秘密、在这些秘密上定义 ACL、在访问秘密时强制执行生物特征身份验证,并且重要的是,它可以防止其他应用程序读取秘密。

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

表示凭据的凭据储物柜。储物柜的内容特定于应用程序或服务。应用程序和服务无权访问与其他应用程序或服务关联的凭据。

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

于 2021-09-21T17:14:42.667 回答