37

根据 MSDN,SecureString内容被加密以提高安全性,因此如果将程序交换到磁盘,则无法嗅探字符串内容。

我想知道这种加密怎么可能?该算法将是固定的,因此要么是众所周知的,要么是可扣除的(比如工业算法中广泛使用的七种算法之一),并且程序中的某处必须有一个密钥。因此,攻击者可以获取加密字符串、获取密钥并解密数据。

这种加密如何有用?

4

4 回答 4

21

我引用了一篇关于用于派生密钥的DPAPI的文章。这应该可以回答您对 SecureString 的大多数问题。

是的,SecureString 有缺点并且不完全安全,有一些方法可以访问数据,例如,在 MSDN 上提到将Hawkeye注入进程作为提取 SecureString 的一种方法。我没有亲自验证过这种说法。

DAPI 密钥管理

DAPI 是一种基于对称的加密技术,这意味着它使用相同的密钥来加密和解密数据。在介绍如何使用 DAPI 的一些示例之前,有必要介绍一下 DAPI 如何管理其密钥。在大多数情况下,DAPI 密钥管理过程是不可见的,您通常不需要担心它,这也是 DAPI 是一种好方法的主要原因。

在介绍中我写道,主密钥是从用户的登录密码生成的。这不是完整的画面。实际发生的是 Windows 使用用户的登录密码来生成主密钥。此主密钥使用用户的密码进行保护,然后与用户的配置文件一起存储。然后,此主密钥用于派生许多其他密钥,而这些其他密钥用于保护数据。

Windows 这样做的原因是它允许应用程序在生成单个密钥的过程中添加称为熵的附加信息。您会看到如果在用户的登录帐户下运行的每个应用程序都使用相同的密钥,那么每个应用程序都可以取消保护 DAPI 保护的数据。有时您可能希望应用程序能够共享受 DAPI 保护的数据;但是,有时您不会。通过让应用程序为密钥的生成贡献熵,那么该密钥就成为特定于应用程序的,并且受该应用程序保护的任何数据只有在他们知道熵的情况下才能再次不受保护。

尽管生成主密钥,然后使用该主密钥生成其他密钥来进行实际加密,这似乎是一种冗长的方法,但它确实有一个主要优势。由于用户密码保护的主密钥和用于保护数据的实际密钥之间存在额外的抽象级别,这意味着当用户更改密码时,只需要重新保护主密钥;无需重新保护任何受保护的数据。由于主密钥的大小比数据小得多,因此可以显着节省性能。

当用户的密码更改时,当然会生成一个新的主密钥。然后使用这个新的主密钥来生成新的个人密钥。但是,由于所有先前生成的单独密钥都是从旧主密钥派生的,因此 Windows 需要存储所有以前的主密钥,它确实如此。Windows 永远不会忘记主密钥,并且所有受保护的数据都标有 GUID,该 GUID 指示用于保护数据的主密钥。因此,就适应性而言,DAPI 能够应对用户密码的更改,同时确保 a) 受保护的数据不需要重新保护,b) 用于先前保护数据的密钥仍然可用,以及 c ) 它会自动为您完成所有这些工作。

除非计算机是域的成员,否则 DAPI 只能在用于保护它的同一台计算机上不受保护的数据。

除了允许用户级别的保护,因为主密钥基于用户密码并且一个用户的受保护数据不能被另一个用户取消保护,DAPI 还提供机器级别的保护,因为主密钥基于机器特定的信息。机器级主密钥允许应用程序存储受保护的数据,使其不受应用程序所有用户的保护。已经描述的过程中的唯一区别是主密钥是从机器特定信息而不是用户特定信息生成的。

于 2011-08-08T12:52:19.037 回答
11

我看了一点它的代码,它使用 Windowsadvapi32来完成它的脏活。所以密钥不会存储在应用程序的内存中。

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static int SystemFunction040([In, Out] SafeBSTRHandle pDataIn, [In] uint cbDataIn, [In] uint dwFlag)

更广为人知的是RtlEncryptMemory.

RtlDecryptMemory它用( SystemFunction041)解密。

我确信编译器也会对 做一些事情SecurityCriticalAttribute

编辑这反映在 4.0. 其他版本可能不同。

于 2011-08-08T12:39:18.137 回答
8

正如其他人已经回答的那样,内容SecureString是使用 DPAPI 加密的,因此密钥不会存储在您的应用程序中,它们是操作系统的一部分。我不是 100% 肯定的,但我会假设它SecureString使用用户特定的密钥,因此即使另一个进程获得对内存块的访问权限,它也必须在相同的凭据下运行才能简单地解密使用 DPAPI 的内容。即使不是,机器密钥(理论上)也可以防止字符串在传输到另一个系统时被轻易解密。

更重要的SecureString是如何以及何时使用它。它应该用于存储需要在内存中保留“延长”时间段的字符串数据,但在解密形式中并不经常需要这些数据。在某些时候,您将不得不将其解密为普通的 oldSystem.StringSystem.Char[]. 这是它在记忆中最脆弱的时候。如果你经常这样做,那么你有多个解密字符串的副本漂浮在内存中等待收集。

作为一般规则,如果我正在读取需要保留以供不经常使用的加密数据(例如登录凭据)(例如,PayPal 或 Amazon API 交互),那么我将这些凭据存储/缓存为SecureString,然后将其解密为 -只需要足够长的时间来进行 Web 服务调用,并确保任何解密副本的生命周期只有几行代码。

使用关键块或类似内容向 CLR 提示它在使用解密字符串时不应进行上下文切换可能也是明智之举,以提高在缓存或交换内存之前收集任何解密副本的机会。

于 2011-08-08T13:37:03.767 回答
2

借助 DPAPI 的魔力:

此类使用数据保护 API (DPAPI) 受保护的内存模型存储其数据。换句话说,数据总是以加密形式存储在 SecureString 中。加密密钥由本地安全权限子系统(LSASS.EXE)管理,通过DPAPI,可以通过进程间通信对数据进行解密。

于 2011-08-08T12:53:21.610 回答