没有SecureString的情况下如何保护字符串?

时间:2019-04-09 10:38:16

标签: c# encryption securestring

用例是在c#中的内存编程中保护字符串。 Microsoft本身不鼓励使用SecureString(https://docs.microsoft.com/en-us/dotnet/api/system.security.securestring?view=netframework-4.7.2)类。

我想知道它是否可以替代:

  • 将字符串转换为字节数组,然后立即将字符串设置为null(并最终调用垃圾收集器),
  • 使用ProtectedMemory类对字节数组进行加密。

有什么建议吗?

4 个答案:

答案 0 :(得分:3)

SecureString类无可替代。微软鼓励使用的“替代方案” here

  

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

因此,如果您真的需要凭据并且没有其他方法:在.NET Framework上,使用SecureString。对于.NET Core,目前没有其他选择。

答案 1 :(得分:3)

因此,您要问的基本问题是“由于Microsoft不鼓励使用SecureString的用户,我可以自己推出吗?”。

好吧,除了您的实现可能不如Microsoft原始版本安全之外,它还会至少存在相同的问题,因为它们不是针对特定的实现,而是针对这个概念。

如果要使用该概念,则也可以使用SecureString。解决方案是不要在内存中使用加密凭据的概念,既不要与Microsoft的类一起使用,也不要与您自己的自制软件一起使用。

答案 2 :(得分:2)

我不会说它“被Microsoft吓坏了”-这太过简单了。实际的原因在本页(https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md中给出了,该参数似乎是“在.NET Core中使用它不值得”,也不是说它总体上并不安全。 >

我认为SecureString 是安全的……但仅适用于Windows上的.NET Framework。我链接到的页面来自跨平台的.NET Core项目-因此,劝阻或禁止在.NET Core中使用SecureString是有意义的-但是,如果您的项目针对的是.NET Framework(是Windows专有的)或针对Windows的.NET Core -那么就可以了。引号如下(强调我的意思):

  

该数组的内容未加密 .NET Framework上的除外。

顺便说一句,{TW} SecureString可以安全地用于避免内存中的明文,如果您仅通过使用其SecureString方法将秘密直接直接读取到Append中。从控制台读取密码(伪代码)时,这非常有用:

Console.WriteLine( "Enter your password" );
SecureString password = new SecureString();
while( Char c = Console.ReadKey() != '[Enter'] ) {
    password.Append( c );
}

...但是,如果以后需要访问该字符串的明文版本,则它的安全性较低(尽管希望明文字符串可以由GC收集为第0代对象)。

关于您的建议:

  
      
  • 将字符串转换为字节数组,然后立即将字符串设置为null(并最终调用垃圾收集器)
  •   
  • 使用ProtectedMemory类对字节数组进行加密。
  •   

这确实是SecureString的工作方式,它仍然遇到相同的问题:加密内容的明文副本在内存中仍然存在很短的时间-这就是问题所在。

答案 3 :(得分:0)

tl;dr:SecureString 是个好主意。微软不再推荐它的原因是.NET Core不能拥有它,因为Linux不支持加密。

简答

  • 使用字符数组
  • 加密

长答案

您需要 SecureString 的原因有两个。

第一个是 HeartBleed 攻击的思路,原因是 SecureZeroMemory 存在,以及 Windows 在给你之前总是归零一页内存的原因:避免泄漏信息。

<块引用>

奖励阅读

终端服务在 RAM 中保持未加密的用户密码。

第二个,也是 SecureString 存在于 .NET 中的原因,是因为您无法调用 SecureZeroMemory。 .NET 中的字符串是不可变的,您无法控制它们的生命周期。

所以为了解决这个问题,有两个要素:

  1. 您可以切换到字符数组

当它是一个数组时,意味着您可以擦除内容。这意味着当您完成敏感的信用卡号、比特币私钥、密码、英特尔蓝光主密钥时,您可以做与 ZeroMemory 相同的道德行为:您可以擦除它们

这解决了完全无法擦除C#字符串

的问题
  1. 使用 CryptProtectData 加密

另一个,无关,SecureString 的优点是原始字符串不会出现在内存转储、虚拟机 RAM 快照、Web 服务器日志、调试器监视窗口中。或者在HeartBleed 攻击的情况下,不会出现在分发给其他网站用户的未初始化数据中。

这是 SecureString 提供的两个核心元素。

两者都可以复制。 .NET Core 没有它们的原因并不是因为 SecureString 是一个坏主意、一个浪费的主意、一个不完整的主意,或者 “没有兑现承诺”。相反,这是因为 Linux 没有 CryptProtectData 的等价物。由于 .NET Core 必须是跨平台的,因此它们必须满足最小公分母的需求。所以他们举起手说把它拿掉。

但 SecureString 与以下概念一样有效:

  • 零内存
  • SecureZeroMemory
  • Windows 将一页 RAM 归零,然后再将其提供给您的进程
  • 为什么当您 SetFileValidData 时,Windows 会将文件内容清零

否则任何人都在说谎。

提醒 - 您要使用 SecureString

您想使用 SecureString

这是否意味着信用卡号在某些时候以明文形式存在于内存中:

<块引用>

不,原因是它的用途非常有限,并且在大多数情况下,该字符串要么保留在内存中,要么重新出现。原始字符串保留在内存中,直到它成为 GCd。并且由于 SecureString 在 Win32 API(或 Linux)中没有对应项,一旦应用程序尝试对其执行任何操作,原始字符串就会重新出现。即使在 SDK 中,也只有 NetworkCredentials 正确使用了它,而 SecureString 不是 Windows 概念,所以一旦你使用 Windows API,它就会被转换回来。

  • 是的,信用卡号曾在内存中以明文形式存在。
  • 是的,用户的 bitlocker 密码曾在内存中。
  • 是的,iOS 用户的 PIN 码曾在内存中。
  • 是的,比特币私钥曾在内存中。
  • 是的,加密页面文件中的数据在某些时候在 RAM 中未加密。

但我们必须意识到的是:

  • 这并不意味着你不应该从记忆中抹去它
  • 这并不意味着您不应该阻止它出现在日志文件中
  • 这并不意味着您不应该阻止它出现在交换文件中
  • 这并不意味着您不应该阻止它出现在故障转储文件中
  • 这并不意味着您不应该阻止它出现在监视窗口中
  • 这并不意味着您不应该阻止它出现在快照中

这些是好的纵深防御措施。

任何人说不同的都是错误的。