将字符串转换为安全字符串

184

如何将 String 转换为 SecureString

15个回答

256

还有一种方法可以在 SecureStringString 之间进行转换。

1. String to SecureString

SecureString theSecureString = new NetworkCredential("", "myPass").SecurePassword;

2. 将SecureString转换为String

string theString = new NetworkCredential("", theSecureString).Password;

这是链接


5
到目前为止,这是最常见用例的最佳解决方案! - Dan Bechard
28
如果您的代码创建了一个带有想要保护的值的string对象,那么使用SecureString就没有意义了。 SecureString的目标是避免在托管内存中拥有字符串,因此,攻击者检查该内存时可以发现您要隐藏的值。由于您调用的NetworkCredential构造函数需要一个字符串,所以这不是正确的方法... 当然,您的代码是将SecureString转换成string和反之的简单方法,但使用它使得您的代码与使用简单的string一样安全。 - Gian Paolo
31
@GianPaolo,虽然在理论上这是正确的,但在实践中,我们仍然需要在使用某些库时使用“SecureString”。而且让我们诚实点,如果有人正在积极扫描您应用程序的内存,那么您已经输了。即使您已经正确使用了SecureString(我从未见过),也会有其他有价值的数据可以提取。 - Jonathan Allen
1
@JonathanAllen:你可能是对的,但这只是因为我们有一种把安全视为事后处理的文化。如果你正在创建一个食谱网站,朝鲜不太可能尝试扫描你程序的内存。如果你正在编写银行软件,威胁就会更加真实。 - Eric J.
1
非常有用的转换。当然,我正在使用SecureString来构建安全软件以存储密码,但为了与旧系统或简单系统兼容,我们还需要支持普通字符串中的密码,否则我会被解雇 :-) - Roland
显示剩余3条评论

165

你不需要这样做。使用SecureString对象的主要原因是避免创建字符串对象(该对象会在内存中加载并以明文形式保留,直到进行垃圾收集)。但是你可以通过追加字符来向SecureString添加字符。

var s = new SecureString();
s.AppendChar('d');
s.AppendChar('u');
s.AppendChar('m');
s.AppendChar('b');
s.AppendChar('p');
s.AppendChar('a');
s.AppendChar('s');
s.AppendChar('s');
s.AppendChar('w');
s.AppendChar('d');

29
这个组合是“12345”……这就是白痴会设置在自己行李上的那种密码! - Kellen Stuart
18
我看到的只有 ***** - Ian Boyd
AppendChar 在运行时才执行,所以这些字符仍然可以从 IL 中读取,对吧?也许一些编译时的混淆能够帮上忙,如果你是在硬编码密码的话。 - samus
9
那是一个站不住脚的论点。你从哪里获得密码?可以来自表单或命令行。除非他们创建一种直接从设备或命令行参数读取安全字符串的方法,否则我们需要将字符串转换为SecureString。 - Quark Soup
3
@DonaldAirey 你从一个密码管理器中获取密码。一般来说,我们在这里谈论的密码不是用户密码,而是可能导致特权升级或数据泄露的东西。这意味着这些密码是为其他服务而设,不应该通过命令行或明文传输到网络通道中。 - Barry Kelly

81

下面的方法可帮助将字符串转换为安全字符串

private SecureString ConvertToSecureString(string password)
{
    if (password == null)
        throw new ArgumentNullException("password");

    var securePassword = new SecureString();

    foreach (char c in password)
        securePassword.AppendChar(c);

    securePassword.MakeReadOnly();
    return securePassword;
}

38
+1,但请注意将密码作为字符串参数传递会在托管内存中创建一个未加密的字符串实例,这将使“SecureString”的作用失效。只是为了以后使用此代码的人提醒一下。 - Cody
29
@Cody说得对,这是个好观点,但我们许多人并不关心SecureString是否安全,只是因为某些Microsoft API需要它作为参数而使用它。 - Dan Bechard
9
原来即使是SecureString类也无法保持字符串加密,这就是为什么微软正在考虑废弃该类型的原因。https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md - Martin Brown

25

你可以按照以下步骤进行:

string password = "test";
SecureString sec_pass = new SecureString();
Array.ForEach(password.ToArray(), sec_pass.AppendChar);
sec_pass.MakeReadOnly();

17

这里有一个简单的 LINQ 技巧。

            SecureString sec = new SecureString();
            string pwd = "abc123"; /* Not Secure! */
            pwd.ToCharArray().ToList().ForEach(sec.AppendChar);
            /* and now : seal the deal */
            sec.MakeReadOnly();

51
这个答案是一项练习,旨在查看一次性生成多少临时纯文本副本。 - Mike Caron
1
正如@john-dagg所建议的那样,不要使用字符串设置密码,因为如果这样做,使用SecureString就没有任何优势了。在您的情况下,您已经以明文形式输入了密码,稍后使用SecureString并没有保护任何内容。我希望您理解,SecureString旨在保护您的字符串免受查看反汇编或调试器中IL/内存中内容的人的攻击。 - user734028
2
在理想的世界中是正确的。但有时你必须使用字符串。我可以编写所有(分层)代码以SecureString形式,但当我(最终)进行需要客户端密钥(作为纯字符串)的rest调用时......那就是我必须做的。我如何处理这个问题是......等到最后一刻才将其转换为字符串,使用它,并(不真正地)销毁字符串本身,并(尽我所能)销毁SecureString。这是“尽力减少曝光”的最佳尝试。是的,我了解IL /内存“使用情况”。在我看来,Microsoft应该将此对象命名为“KindaSecureString”。 - granadaCoder
我的先前评论在这里讨论:https://docs.microsoft.com/en-us/dotnet/api/system.security.securestring?view=net-5.0#HowSecure。"存储与使用 更一般地说,SecureString类为应保护或保密的字符串值定义了一种存储机制。但是,在.NET Framework本身之外,没有任何使用机制支持SecureString。这意味着安全字符串必须转换为可用形式(通常是明文形式),以便其目标可以识别,并且解密和转换必须在用户空间中发生。" #cantVetoEverything - granadaCoder

13
我只想指出,所有声称 "这不是 SecureString 的重点" 的人都应该知道,许多人提出这个问题可能处于某种应用程序中,无论出于任何原因,他们并没有特别关心将密码的临时副本存在可以GC的堆上,但他们必须使用一个仅接受 SecureString 对象的 API。因此,你有一个应用程序,你并不在意密码是否在堆上,也许它仅供内部使用而密码只存在于其底层网络协议所需,但你发现存储密码的字符串无法用于例如设置远程 PowerShell Runspace——但没有简单、直接的一行代码来创建所需的 SecureString。这只是一个小麻烦——但为了确保那些真正需要SecureString的应用程序不会诱使作者使用System.String或System.Char[] 中介,这可能是值得的。 :-)

10

我想提出一些看法。为什么呢?

你不能仅仅将所有字符串更改为安全字符串,然后突然间使你的应用程序“安全”了。安全字符串的设计目的是尽可能地保持字符串加密,并只在非常短的时间内进行解密,在操作完成后会清除存储器。

我认为,在担心保护应用程序字符串之前,您可能需要解决一些设计层面上的问题。请给我们更多关于您正在尝试做什么的信息,我们也许能够更好地帮助您。


2
我只想使用它来为ProcessStartInfo设置密码并以不同的用户身份运行我的exe.. 这对我起作用了... - Developer404
6
我的应用程序通过在安全系统中运行来获得保护。我不太关心这个特定的字符串是否在内存中加密。如果系统被攻击,有更重要的信息需要担心。SecureString只是我的程序需要使用的Microsoft API所必需的。开发人员应该考虑自己的用例并确定在上下文中将字符串转换为SecureStrings是否是一个有效的操作。 - Dan Bechard
是的,我同意。这篇文章片段稍微涉及了一下。https://docs.microsoft.com/en-us/dotnet/api/system.security.securestring?view=net-5.0#HowSecure存储与使用 更普遍地说,SecureString类定义了一种用于保护或保密字符串值的存储机制。然而,在.NET Framework之外,没有任何使用机制支持SecureString。这意味着安全字符串必须转换为可用形式(通常是明文形式),以便其目标可以识别,并且解密和转换必须在用户空间中进行。 - granadaCoder
总的来说,SecureString比String更安全,因为它限制了敏感字符串数据的暴露。然而,这些字符串仍可能会被任何具有原始内存访问权限的进程或操作所暴露,例如在主机计算机上运行的恶意进程、进程转储或用户可查看的交换文件。建议使用不透明句柄来保护密码,而不是使用SecureString,这些凭据存储在进程外部。 - granadaCoder
但基本上,你不能“总是找到另一种方法”。我的例子是,在你的dotnet代码中有层和类和方法,你传递一个SecureSTring……但是如果我最终必须调用Oauth/STS……那就需要一个普通字符串“client_secret”……在那个时候,我“必须要有一个字符串”。我处理这个问题的方式是……等到最后一刻才将其转换为字符串,使用它,并(不是真正地)销毁字符串本身,并(尽我所能)销毁SecureString。这是“尽力减少曝光”的最佳尝试。是的,我理解IL/内存“使用”。 - granadaCoder
显示剩余4条评论

10
unsafe 
{
    fixed(char* psz = password)
        return new SecureString(psz, password.Length);
}

6

不需要花哨的Linq,也不需要手动添加所有字符,只需要简单明了:

var str = "foo";
var sc = new SecureString();
foreach(char c in str) sc.appendChar(c);

甚至更加花哨,无需定义变量。 - Ahmed Fwela
这是一行代码: var sc = new SecureString(); foreach(char c in "foo") sc.appendChar(c); - Ahmed Fwela

4
以下两个扩展程序可以解决这个问题:
  1. For a char array

    public static SecureString ToSecureString(this char[] _self)
    {
        SecureString knox = new SecureString();
        foreach (char c in _self)
        {
            knox.AppendChar(c);
        }
        return knox;
    }
    
  2. And for string

    public static SecureString ToSecureString(this string _self)
    {
        SecureString knox = new SecureString();
        char[] chars = _self.ToCharArray();
        foreach (char c in chars)
        {
            knox.AppendChar(c);
        }
        return knox;
    }
    
感谢John Dagg提供AppendChar建议。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接