System.Security.SecureString 类

重要

建议不要将 SecureString 类用于 .NET(Core)上的新开发,或者将现有代码迁移到 .NET(Core)。 有关详细信息,请参阅 不应使用 SecureString

本文提供了此 API 参考文档的补充说明。

SecureString 是一种提供安全性度量值的字符串类型。 它尝试避免将潜在的敏感字符串作为纯文本存储在进程内存中。 (但是,有关限制,请参阅 SecureString 有多安全? 部分。 SecureString 初始化实例时或修改值时,使用基础平台支持的机制自动保护实例的值。 应用程序可以通过调用 MakeReadOnly 该方法来呈现实例不可变并防止进一步修改。

实例的最大长度 SecureString 为 65,536 个字符。

重要

此类型实现 IDisposable 接口。 在使用完类型的实例后,应直接或间接释放类型。 若要直接释放类型,请在块中Disposetry/调用其catch方法。 若要间接释放它,请使用语言构造,例如 using (在 C# 中)或 Using (在 Visual Basic 中)。 有关详细信息,请参阅接口主题中的 IDisposable “使用实现 IDisposable 的对象”部分。

SecureString 及其成员对 COM 不可见。 有关详细信息,请参阅 ComVisibleAttribute

字符串与 SecureString

类的 System.String 实例既不可变,也不再需要时,无法以编程方式计划垃圾回收;也就是说,实例在创建后是只读的,并且无法预测实例何时将从计算机内存中删除。 由于 System.String 实例是不可变的,因此那些看似在修改现有实例的操作实际上会创建该实例的副本进行操作。 因此,如果 String 对象包含敏感信息(如密码、信用卡号或个人数据),则使用密码、信用卡号或个人数据等敏感信息时,可能会显示信息的风险,因为应用程序无法从计算机内存中删除数据。

SecureString 对象与 String 对象相似的是它们都有文本值。 但是,对象的值 SecureString 固定在内存中,可以使用保护机制(如基础作系统提供的加密)进行修改,直到应用程序将其标记为只读,并且可由调用 Dispose 该方法的应用程序或 .NET 垃圾回收器从计算机内存中删除。

有关SecureString类的局限性讨论,请参阅SecureString有多安全?部分。

SecureString 操作

SecureString 类包括允许执行以下操作的成员:

实例化SecureString对象。您可以通过调用无参数构造函数来实例化SecureString对象。

可以通过调用SecureStringSecureString方法,将字符逐个添加到AppendChar对象中。

重要

SecureString不应从某个String对象构造,因为敏感数据已受到不可变String类的内存持久性后果的影响。 构造 SecureString 对象的最佳方式是从处理每个字符的非托管源(例如使用 Console.ReadKey 方法)中进行构建。

SecureString对象中删除字符:可以通过调用SetAt方法替换单个字符、通过调用RemoveAt该方法删除单个字符,或者通过调用SecureString该方法从Clear实例中删除所有字符。

SecureString使对象只读一旦定义了该对象所表示的SecureString字符串,则调用其MakeReadOnly方法以使字符串只读。

获取有关 SecureString 对象的信息: SecureString 类只有两个成员提供有关字符串的信息:其 Length 属性指示字符串中 UTF16 编码的代码单元的数目;以及 IsReadOnly指示实例是否为只读的方法。

释放分配给实例的 SecureString 内存,因为 SecureString 实现 IDisposable 接口,因此可以通过调用 Dispose 该方法释放其内存。

SecureString 类没有检查、比较或转换值 SecureString的成员。 没有此类成员有助于保护实例的价值免受意外或恶意泄露。 使用 System.Runtime.InteropServices.Marshal 类的合适成员(例如 SecureStringToBSTR 方法)来操作 SecureString 对象的值。

.NET 类库通常通过以下方式使用 SecureString 实例:

SecureString 和互操作

由于作系统不直接支持 SecureString,因此在将字符串传递给本机方法之前,必须将对象的值 SecureString 转换为所需的字符串类型。 该 Marshal 类有五种方法可以执行此作:

其中每个方法在非托管内存中创建一个明文字符串。 开发人员有责任在内存不再需要时立即将其置零并释放。 每个字符串转换和内存分配方法都有一个相应的方法来零出并释放分配的内存:

分配和转换方法 零和 free 方法
Marshal.SecureStringToBSTR Marshal.ZeroFreeBSTR
Marshal.SecureStringToCoTaskMemAnsi Marshal.ZeroFreeCoTaskMemAnsi
Marshal.SecureStringToCoTaskMemUnicode Marshal.ZeroFreeCoTaskMemUnicode
Marshal.SecureStringToGlobalAllocAnsi Marshal.ZeroFreeGlobalAllocAnsi
Marshal.SecureStringToGlobalAllocUnicode Marshal.ZeroFreeGlobalAllocUnicode

SecureString 有多安全?

正确创建时, SecureString 实例提供的数据保护比一个 String多。 从处理每个字符源创建字符串时,String 在内存中创建多个中间层,而 SecureString 只创建一个实例。 String对象的垃圾回收是非确定性的。 此外,由于内存未固定,垃圾回收器将在移动和压缩内存时生成String值的额外副本。 相比之下,分配给 SecureString 对象的内存将被固定,并且可以通过调用 Dispose 该方法释放内存。

尽管存储在实例中的数据 SecureString 比存储在实例中的数据 String 更安全,但实例的安全性 SecureString 存在重大限制。 这些包括:

平台

在 Windows操作系统上,实例SecureString的内部字符数组的内容被加密。 但是,无论是由于缺少 API 还是密钥管理问题,加密在所有平台上都不可用。 由于此平台依赖项, SecureString 因此不会加密非 Windows 平台上的内部存储。 这些平台上使用其他技术来提供额外的保护。

持续时间

即使 SecureString 的实现能够利用加密,分配给 SecureString 实例的明文也可能在各种时间被暴露。

  • 由于 Windows 在作系统级别不提供安全字符串实现,因此 .NET 仍需将安全字符串值转换为其纯文本表示形式才能使用它。

  • 每当安全字符串的值由诸如 AppendCharRemoveAt这样的方法修改时,必须解密它(即转换回纯文本),修改,然后再次加密。

  • 如果在互作调用中使用安全字符串,则必须将其转换为 ANSI 字符串、Unicode 字符串或二进制字符串(BSTR)。 有关详细信息,请参阅 SecureString 和互作 部分。

SecureString类相比,String实例值公开的时间间隔只是缩短。

存储与使用的对比。更一般地说,SecureString 类定义了一个存储机制,用于保护或保密的字符串值。 但是,在 .NET 本身之外,没有使用机制支持 SecureString。 这意味着安全字符串必须转换为可由其目标识别的可用形式(通常是明文形式),并且解密和转换必须在用户空间中发生。

总的来说, SecureStringString 限制敏感字符串数据的公开更安全。 但是,这些字符串仍可能暴露给有权访问原始内存的任何进程或作,例如在主计算机上运行的恶意进程、进程转储或用户可查看的交换文件。 为了更好地保护密码,建议的替代方法是使用不透明句柄来处理存储在进程外部的凭据,而不是使用 SecureString