WPF PasswordBox的安全性到底有多高?

5

最近我已经问了几个关于PasswordBox的问题,但是在我的问题核心上,我需要一种极其安全的方法来让用户输入非常敏感的信息到我的.Net应用程序中。

开箱即用,WPF PasswordBox是获取密码或其他敏感信息的控件。出于安全考虑,它通过SecurePassword属性提供了一个SecureString对象,这对我来说足够安全。然而,我发现这个控件有一个主要缺陷——它有一个Password属性,该属性是不安全的.Net字符串中的用户输入内容。

我想假设如果我从未在我的应用程序中访问Password属性,则不会生成敏感信息的不安全版本并且不必进行垃圾回收。那很好。但是,由于保护字符串的安全形式的目的是使值免受可以读取.Net内存的窥视进程(我假设)的保护,因此相同的窥视恶意代码是否可以简单地查找挂在内存中的PasswordBox并找到某种方法来访问Password字段,使SecureString值的使用基本无效?

我承认,我不知道这些漏洞是如何执行的。但是,如果问题是某个应用程序可能在.Net内存管理器/垃圾收集器中嗅探您的变量,那么对我来说似乎很有道理,他们只需要访问PasswordBox控件对象,而不是内存中的字符串对象,并简单地访问Password属性。我在这个难题中漏掉了什么?如果PasswordBox包含明文的Password属性,它又怎么算是安全的呢?


1
以前有WinSpy可以显示控件和可见属性,如果有类似的东西我不会感到惊讶。我会给你我在类似主题上阅读得很多的词汇。如果人们想要破解它,他们就会这样做。除非是单向加密并且仅比较加密项目而不是未加密项目,否则几乎没有办法使用种子和代码加密。因为如果有一个固定的种子,它就在你的代码中可以找到;如果它不是固定的,那么它来自某个地方并且可以被拦截或转移,你越努力,他们就越努力。 - BugFinder
2个回答

9
PasswordBox.Password属性将从SecurePassword属性创建.NET字符串,而不将其作为字符串存储在内部。使用SecureString的整个目的是减少数据存在于内存中的时间,并减少敏感数据的副本数量。您可以在文档中了解更多有关SecureString的信息。

SecureString是加密的(如果可能,通常是这样),因此如果有人可以读取原始内存(例如某人窃取了您的内存转储),他将无法从中读取密码。如果您的应用程序被攻击者注入自己的dll并在那里运行任意代码,那么您已经有了更大的问题(并且仍然很有可能密码不再存在于内存中)。

话虽如此,当使用PasswordBoxSecureString时,您仍然可以遵循一些合理的指导方针。

  1. Always dispose result of PasswordBox.SecurePassword if you ever need to access it:

    using (var pwd = myBox.SecurePassword) {
        // do stuff
    }
    

这看起来可能有些奇怪(处理返回的属性),但应该这样做,因为此属性返回SecureString的副本,本应该是一个方法而不是属性,但由于某种原因变成了属性。

  1. 尽可能少访问Password属性,最好只在真正需要时访问一次。您不希望密码的副本在内存中停留的时间更长。

  2. 完成后始终调用yourBox.Clear()。这将设置密码为空字符串并清除内部的SecureString内存。


啊,我明白了。第三点可能解决了我的疑虑。我的想法是PasswordBox可能会作为某种可访问控件而停留在GC中。Clear()方法可能会清除内容,以便即使控件对象挂起一段时间,SecureString也将变得无用。 - RLH
1
@RLH 是的,但即使在调用Clear之前,攻击者要访问SecureString内部的密码也需要在您的进程中执行代码(例如调用Password属性),此时您已经注定会失败。而且SecureString是加密的。因此,只有明文密码(从调用Password`获得)如果在内存中停留时间过长,则会受到威胁。 - Evk
我明白了,这有助于澄清情况。我认为我已经掌握了足够的信息,可以做到我需要做的事情。 - RLH
我可以并且确实提到了一个用途,那就是获取密码短语。具体来说,我们从不存储该值,但我们确实将其用于本地化加密。我现在正在努力保护从密码短语中派生的加密密钥在内存中的安全性。这是过程的一部分。我们的工具有许多层面。 - RLH
@RLH 好的,我只是想提一下,框架的某些部分接受 SecureString 而不需要你将其转换为普通字符串。例如,SqlConnection 有一个 Credential 属性可以接受它:connection.Credential = new SqlCredential("user id", mySecureString); - Evk
显示剩余3条评论

1

您不必猜测,只需查找即可。

密码框中没有Password字段。它有一个名为PasswordTextContainer的字段来保存密码。

这意味着文档是正确的,即:

备注

当获取 Password 属性值时,会将密码作为明文暴露在内存中。为避免此潜在安全风险,请使用 SecurePassword 属性将密码作为 SecureString 获取。

如果您不将其视为纯文本并将其保留为纯文本,则它不会是纯文本。

但再次提醒,不要相信互联网上的随机人员(包括我!),自己查看


不,你误解了重点。我已经阅读了文档。然而,PasswordBox控件是内存中的一个对象。如果恶意代码可以读取.Net内存或GC并找到PasswordBox控件,它不能访问Password属性吗?当然,该属性可能会执行提取数据的工作,但是如果您可以访问内存中的对象,则可能仍然可以调用该属性,并且.Net运行时将为您提取值。我可能错了。我不知道漏洞是如何工作的。但是,任何形式的提供此值的属性都似乎存在问题。 - RLH
@RLH 如果您有一个可以篡改内存并在其中执行代码的进程,那么就没有办法保持安全。拥有这种特权,没有任何东西是安全的。 - nvoigt
大部分是真的。我认为唯一可能稍微缓解的方法是,如果该进程在用户级别使用某种形式的DPAPI加密。只要恶意代码不在给定用户的上下文中运行,并且DPAPI API没有进行本地机器加密,就会有一个额外的屏障阻止解密。然而,我完全同意。无论如何,即使机器被攻击,这个应用程序也需要尽其所能保护这些数据。这肯定是一场猫鼠游戏,但我们希望使其非常非常孤立,以便如果存在可利用的区域,它也是非常难以攻破的。 - RLH

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