System.Security.SecureString 类

重要

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

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

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

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

重要

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

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

字符串与 SecureString

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

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

有关类限制SecureString的讨论,请参阅 SecureString 如何安全?

SecureString 操作

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

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

向对象添加字符SecureString:可以通过调用AppendChar对象或InsertAt方法,一次SecureString向对象添加一个字符。

重要

SecureString不应从某个String对象构造,因为敏感数据已受到不可变String类的内存持久性后果的影响。 构造 SecureString 对象的最佳方式是从一次字符非托管源(如 Console.ReadKey 该方法)中获取的。

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

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 类有五种方法可以执行此操作:

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

分配和转换方法 零和免费方法
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 和互操作 部分。

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

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

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