我什么时候需要.NET 中的 SecureString?

我正在尝试理解.NET 的 SecureString 的用途:

系统的实例。字符串类是不可变的,当不再需要时,不能以编程方式调度垃圾收集; 也就是说,实例在创建之后是只读的,无法预测何时从计算机内存中删除该实例。因此,如果 String 对象包含敏感信息,比如密码、信用卡号或个人数据,那么在使用该对象之后,由于应用程序无法从计算机内存中删除数据,因此该信息可能会被泄露。

SecureString 对象类似于 String 对象,因为它具有文本值。但是,SecureString 对象的值是自动加密的,可以被修改,直到应用程序将其标记为只读,并且可以通过应用程序或。NETFramework 垃圾回收器。

当实例初始化或修改值时,SecureString 实例的值将自动加密。应用程序可以通过调用 MakeReadOnly 方法使实例呈现为不可变的并防止进一步修改。

自动加密是最大的收益吗?

为什么我不能说:

SecureString password = new SecureString("password");

而不是

SecureString pass = new SecureString();
foreach (char c in "password".ToCharArray())
pass.AppendChar(c);

我遗漏了 SecureString 的哪个方面?

44169 次浏览

我想这是因为字符串是安全的,也就是说,黑客不应该能够读取它。如果用字符串初始化,黑客就可以读取原始字符串。

我相信之所以必须使用字符追加而不是一个平面实例化,是因为在后台向 SecureString 的构造函数传递“ password”时,会将“ password”字符串放在内存中,从而破坏了安全字符串的作用。

通过附加,您只是把一个字符在一个时间到内存中,这是可能不相邻的其他物理使它更难以重建原始字符串。我可能是错的,但是他们是这么跟我解释的。

该类的用途是防止通过内存转储或类似工具公开安全数据。

微软发现,在某些情况下,导致服务器(桌面,无论什么)崩溃,有时执行期函式库会做一个内存转储,暴露内存中的内容。SecureString 在内存中对其进行加密,以防止攻击者能够检索字符串的内容。

正如描述中所说的,这个值是加密存储的,这意味着进程的内存转储不会显示字符串的值(如果没有一些相当严肃的工作)。

之所以不能从常量字符串构造 SecureString,是因为这样 在内存中就有了字符串的未加密版本。限制您分段创建字符串可以降低整个字符串同时存储在内存中的风险。

SecureString 的一个最大好处是,它可以避免由于页面缓存而将数据存储到磁盘的可能性。如果您在内存中有一个密码,然后加载一个大型程序或数据集,您的密码可能会被写入交换文件,因为您的程序在内存中被分页。使用 SecureString,至少数据不会以明文形式无限期地保存在磁盘上。

在框架的当前版本中,可以合理使用 SecureString 的场景非常少。它实际上只有在与非托管 API 交互时才有用——您可以使用 Marshall 对其进行封送处理。SecureStringToGlobalAllocUnicode.

一旦您将它转换为/从 System.String,您就失去了它的作用。

MSDN样本从控制台输入一次生成一个字符,并将安全字符串传递给非托管 API。这是相当复杂和不现实的。

您可能期望. NET 的未来版本对 SecureString 有更多的支持,这将使它更加有用,例如:

  • 或类似于将控制台输入读入到 SecureString 中,而不需要示例中的所有复杂代码。

  • 将其 TextBox.Text 属性存储为安全字符串的 WinFormsTextBox 替换,以便可以安全地输入密码。

  • 对安全相关 API 的扩展,以允许将密码作为 SecureString 传递。

如果没有以上内容,SecureString 的价值将是有限的。

目前使用 SecureString的框架的一些部分:

主要目的是减少攻击面,而不是消除它。SecureStrings被“固定”在 RAM 中,所以垃圾收集器不会移动它或复制它。它还确保不会将纯文本写入到交换文件或核心转储中。这种加密更像是一种混淆,不会阻止一个坚定的黑客,尽管他能够找到用来加密和解密它的 对称密钥

正如其他人所说的,必须逐个字符创建 SecureString的原因是因为其他方法的第一个明显缺陷: 您可能已经将秘密值作为普通字符串创建了,那么重点是什么呢?

SecureString s 是解决“鸡和蛋”问题的第一步,所以尽管目前大多数情况下需要将它们转换回常规字符串才能使用它们,但它们在框架中的存在意味着将来对它们的更好支持——至少在某种程度上,你的程序不必成为薄弱环节。

编辑 : < em > 不要使用 SecureString

现在的指导说这个课程不应该被使用。详细信息可以在这个链接找到: https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md

摘自文章:

DE0001: 不应该使用 SecureString

动机

  • SecureString的目的是避免在过程中存储秘密 以纯文本形式存储。
  • 然而,即使在 Windows 上,SecureString也不是作为一个操作系统概念存在的。
    • 它只是使得得到纯文本的窗口更短; 它不完全 因为.NET 仍然必须将字符串转换为纯文本 代表。
    • 这样做的好处是,纯文本表示不会被闲置 作为 System.String的实例——本机缓冲区的生存期为 更短。
  • 除了.NETFramework 之外,数组的内容是未加密的。
    • 在.NETFramework 中,内部字符数组的内容是加密的。 NET 也不支持所有环境中的加密 由于缺少 API 或关键的管理问题。

建议

不要对新代码使用 SecureString 数组的内容在内存中没有加密。

处理凭据的一般方法是避免使用凭据 依赖其他方法进行身份验证,如证书或 Windows 认证。

编辑: 原始摘要如下

很多很棒的答案; 这里是已经讨论过的内容的一个快速概要。

微软已经实现了 SecureString 类,努力为敏感信息(如信用卡、密码等)提供更好的安全性。它自动提供:

  • 加密(在内存转储的情况下) 或页缓存)
  • 固定在记忆中
  • 能够标记为只读(以防止任何进一步的修改)
  • 不允许传入常量字符串的安全构造

目前,SecureString 的使用受到限制,但预计将来会得到更好的采用。

根据这些信息,SecureString 的构造函数不应该只是获取一个字符串并将其分割为字符数组,因为拼写出字符串会破坏 SecureString 的用途。

附加信息:

  • 来自.NET 安全性的 邮寄 博客上谈论的内容与 盖在这里。
  • 还有一个是“ nofollow noReferrer” 重新审视它,并提到一个工具 可以转储的内容 SecureString.

编辑: 我发现很难选择最好的答案,因为有很多好的信息,太糟糕了,没有辅助的答案选项。

我会停止使用 SecureString。看来少年组的人不支持了。甚至可能在未来拉它-https://github.com/dotnet/apireviews/tree/master/2015-07-14-securestring

中的所有平台删除 SecureString 中的加密。NET 核心-我们应该过时的 SecureString-我们可能不应该在。NET 核心

简短的回答

为什么我就不能说:

SecureString password = new SecureString("password");

因为现在内存中有了 password,没有办法擦除它——这正是 SecureString的意义所在。

长答案

SecureString之所以存在,是因为在处理完敏感数据后,无法使用 零记忆来擦除它。它的存在是为了解决存在 CLR 的 因为的问题。

在一个常规的 本地人应用程序中,你可以调用 SecureZeroMemory:

用零填充内存块。

注意: SecureZeroMemory 和 ZeroMemory是一样的,除了编译器不会优化之外。

问题是,你 不行调用 ZeroMemorySecureZeroMemory内。NET.进去。NET 字符串是不可变的; 你甚至不能像在其他语言中那样 覆盖字符串的内容:

//Wipe out the password
for (int i=0; i<password.Length; i++)
password[i] = \0;

那你能做什么?我们如何提供。NET 删除密码,或信用卡号码从内存中,当我们用它完成?

这样做的唯一方法是将字符串放在某个 本地人内存块中,然后 可以调用 ZeroMemory。本机内存对象,如:

  • BSTR
  • 一个 HGLOBAL
  • 非托管内存

SecureString 返回丢失的能力

在.NET 中,字符串在使用完后不能被擦除:

  • 它们是不可变的; 你不能覆盖它们的内容
  • 你不能把他们的 Dispose
  • 他们的清理工作完全取决于垃圾收集者

SecureString 作为一种安全传递字符串的方式存在,并且能够在需要时保证它们的清除。

你问了一个问题:

为什么我就不能说:

SecureString password = new SecureString("password");

因为现在内存中已经有了 password,没有办法擦除它。在 CLR 决定重用内存之前,它一直停留在那里。您已经让我们回到了开始的地方; 一个正在运行的应用程序,其密码我们无法摆脱,而且内存转储(或进程监视器)可以看到密码。

SecureString 使用 Data Protection API 将加密的字符串存储在内存中; 这样字符串就不会存在于交换文件、崩溃转储中,甚至不会存在于本地变量窗口中,同事会查看您的应用程序。

我怎么读密码?

那么问题来了: 我如何与字符串交互? 你绝对需要一个像这样的方法:

String connectionString = secureConnectionString.ToString()

因为现在你又回到了原点一个你无法摆脱的密码。您希望 力量开发人员能够正确地处理敏感字符串,以便从内存中删除 可以

这就是为什么.NET 提供了三个方便的 helper 函数来将 SecureString 封装到非托管内存中:

将字符串转换为非托管内存块,处理它,然后再次擦除它。

有些 API 接受 SecureString。例如,在 ADO.net 4.5中,SqlConnection 证件接受一组 SqlCredential:

SqlCredential cred = new SqlCredential(userid, password); //password is SecureString
SqlConnection conn = new SqlConnection(connectionString);
conn.Credential = cred;
conn.Open();

还可以在连接字符串中更改密码:

SqlConnection.ChangePassword(connectionString, cred, newPassword);

里面有很多地方。NET 中继续接受纯字符串以实现兼容性,然后迅速将其转换为一个 put 到 SecureString 中。

如何将文本放入 SecureString?

这仍然留下了一个问题:

首先,我如何将密码输入到 SecureString 中?

这是一个挑战,但关键是要让您考虑安全问题。

有时已经为您提供了功能。例如,WPF 密码箱控件可以直接将输入的密码作为 SecureString返回:

密码属性

获取当前由 密码箱作为 SecureString保存的密码。

这很有帮助,因为无论您在哪里传递原始字符串,现在都会有类型系统抱怨 SecureString 与 String 不兼容。在必须将 SecureString 转换回常规字符串之前,您希望尽可能地延长时间。

转换 SecureString 非常简单:

  • SecureStringToBSTR
  • PtrToStringBSTR

例如:

private static string CreateString(SecureString secureString)
{
IntPtr intPtr = IntPtr.Zero;
if (secureString == null || secureString.Length == 0)
{
return string.Empty;
}
string result;
try
{
intPtr = Marshal.SecureStringToBSTR(secureString);
result = Marshal.PtrToStringBSTR(intPtr);
}
finally
{
if (intPtr != IntPtr.Zero)
{
Marshal.ZeroFreeBSTR(intPtr);
}
}
return result;
}

他们只是不想让你这么做。

但是如何将字符串转换为 SecureString 呢?你首先要做的就是停止在 绳子中使用密码。你需要在 什么的的其他。即使是 Char[]数组也会有所帮助。

这时你就可以添加每个字符 还有清除明文当你完成:

for (int i=0; i < PasswordArray.Length; i++)
{
password.AppendChar(PasswordArray[i]);
PasswordArray[i] = (Char)0;
}

您需要将密码存储在可以擦除的 一些内存中。


Dr: SecureString的存在是为了提供与 零记忆等效的。

有些人看不到 当设备被锁定时,从内存中删除用户的密码或者擦除 经过认证后从内存中删除击键信息的意义,他们不使用 SecureString。

另一个用例是当您使用支付应用程序(POS)时,您只需要使用 不能使用不可变的数据结构来存储敏感数据,因为您是谨慎的开发人员。例如: 如果我将敏感的卡数据或授权元数据存储到不可变的字符串中,那么总是会出现这种情况,即这些数据在丢弃后在内存中可用很长一段时间。我不能简单地覆盖它。另一个巨大的优势是这些敏感数据保存在加密的内存中。