【问题标题】:Convert String to SecureString将字符串转换为安全字符串
【发布时间】:2010-12-06 22:12:40
【问题描述】:

如何将String 转换为SecureString

【问题讨论】:

    标签: c# .net security securestring


    【解决方案1】:

    我会把它扔出去。为什么?

    您不能只是将所有字符串更改为安全字符串,然后您的应用程序突然就“安全”了。安全字符串旨在尽可能长时间地保持字符串加密,并且仅在很短的时间内解密,在对其执行操作后擦除内存。

    在担心保护您的应用程序字符串之前,我会冒昧地说您可能需要处理一些设计级别的问题。向我们提供更多关于您尝试做什么的信息,我们可能会提供更好的帮助。

    【讨论】:

    • 我只想用它来设置 ProcessStartInfo 的密码并以不同的用户身份运行我的 exe。这对我有用...
    • 我的应用程序通过在安全系统上运行而受到保护。我不在乎这个特定的字符串在内存中被加密。如果该系统受到损害,则需要担心有关该系统的重要信息要多得多。我的程序需要使用的 Microsoft API 只需要 SecureString。这意味着开发人员考虑他或她自己的用例并确定将字符串转换为 SecureStrings 是否是上下文中的有效操作。
    • 所以如果他使用像DotPeek这样的反编译器,这种方法不会阻止某人看到密码?在这种情况下有没有办法隐藏字符串?
    • 有几种方法,具体取决于您试图击败的威胁。如果是为了保护除本地管理员以外的任何人的用户信息,您可以使用 Windows 上的 DPAPI 方法来加密文件并在其中存储秘密,将其读入安全字符串,然后将其丢弃。如果是为了公司机密,那么简短的回答是您的应用程序是否可以解密它,用户最终也可以。
    • 是的,我同意。本文片段对此进行了一些介绍。 docs.microsoft.com/en-us/dotnet/api/… 存储与使用 更一般地说,SecureString 类定义了一种存储机制,用于存储应该受到保护或保密的字符串值。但是,在 .NET Framework 本身之外,没有任何使用机制支持 SecureString。这意味着必须将安全字符串转换为其目标可识别的可用形式(通常是明文形式),并且必须在用户空间中进行解密和转换。
    【解决方案2】:

    你没有。使用 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');
    

    【讨论】:

    • “组合是12345...这就是白痴在他的行李箱上的那种东西!”
    • 我看到的都是*****
    • AppendChar 直到运行时才会执行,所以这些字符仍然可以从 IL 中读取,对吗?也许一些编译时混淆也会有所帮助......也就是说,如果你在硬编码密码。
    • 这是一个蹩脚的论点。你从哪里得到密码?从表单或命令行。在他们创建直接从设备或命令行参数读取安全字符串的方法之前,我们需要将字符串转换为 SecureString。
    • @DonaldAirey 您从机密管理员那里获得了密码。一般来说,我们在这里谈论的秘密不是用户密码,而是可能导致权限提升或数据泄露的东西。这意味着密码用于其他服务,不应通过命令行或通过网络通道明文传递。
    【解决方案3】:

    我同意 Spence (+1),但如果您这样做是为了学习或测试 pourpose,您可以在字符串中使用 foreach,使用 AppendChar 方法将每个字符附加到安全字符串。

    【讨论】:

      【解决方案4】:

      以下方法有助于将字符串转换为安全字符串

      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;
      }
      

      【讨论】:

      • +1,但请注意,将密码作为字符串参数传入会在托管内存中创建一个未加密的字符串实例,并且首先破坏了“SecureString”的目的。只是为将来使用此代码的人指出这一点。
      • @Cody 是的,这是一个很好的观点,但我们中的许多人并不关心 SecureString 是否安全,只是因为某些 Microsoft API 需要它作为参数而使用它。跨度>
      • @Cody 事实证明,即使是 SecureString 类也无法对字符串进行加密。这就是为什么 MS 正在考虑淘汰 github.com/dotnet/platform-compat/blob/master/docs/DE0001.md 类型。
      【解决方案5】:

      你可以按照这个:

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

      【讨论】:

        【解决方案6】:
        unsafe 
        {
            fixed(char* psz = password)
                return new SecureString(psz, password.Length);
        }
        

        【讨论】:

          【解决方案7】:

          这是一个廉价的 linq 技巧。

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

          【讨论】:

          • 这个答案是一个练习,看看一次可以生成多少纯文本的临时副本
          • 正如@john-dagg 所建议的那样,这个想法是不要用你的密码设置一个字符串,因为如果你这样做了,使用安全字符串就没有任何优势了。在您的情况下,您已经以纯文本形式输入了密码,但您稍后使用安全字符串不会保护任何内容。我希望您明白,securestring 旨在保护您的字符串免受查看反汇编或调试器的人的影响,因此请查看 IL/内存中的内容。
          • 在理想世界中是正确的。但有时你必须.. 有一个字符串。我可以将我所有的(分层)代码编码为 SecureString,但是当我(最终)进行需要客户端机密(作为纯字符串)的休息调用时……那我就必须这样做。我如何处理这个是.. 等到最后一刻将它变成一个字符串,使用它,并且(不是真的)销毁字符串本身,并且(尽我所能)销毁 SecureString。这是尝试减少曝光的“最佳尝试”。是的,我了解 IL/内存“使用情况”。恕我直言:微软应该将此对象命名为“KindaSecureString”。
          • 我之前的评论在这里讨论:docs.microsoft.com/en-us/dotnet/api/…。 “存储与使用” 更一般地说,SecureString 类为应该受到保护或保密的字符串值定义了一种存储机制。但是,在 .NET Framework 本身之外,没有使用机制支持 SecureString。这意味着必须转换安全字符串转换为可以被目标识别的可用形式(通常是明文形式),并且必须在用户空间中进行解密和转换。” #cantVetoEverything
          【解决方案8】:

          如果您想将stringSecureString 的转换压缩成LINQ 语句,可以如下表示:

          var plain  = "The quick brown fox jumps over the lazy dog";
          var secure = plain
                       .ToCharArray()
                       .Aggregate( new SecureString()
                                 , (s, c) => { s.AppendChar(c); return s; }
                                 , (s)    => { s.MakeReadOnly(); return s; }
                                 );
          

          但是,请记住,使用 LINQ 并不能提高此解决方案的安全性。它存在与从stringSecureString 的任何转换相同的缺陷。只要原始的string 保留在内存中,数据就很容易受到攻击。

          话虽如此,上述语句可以提供的是将SecureString 的创建、使用数据初始化并最终锁定它以防修改。

          【讨论】:

            【解决方案9】:

            没有花哨的 linq,没有手动添加所有字符,简单明了:

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

            【讨论】:

            • 更花哨,不用定义变量
            • 这里有一个班轮:var sc = new SecureString(); foreach(char c in "foo") sc.appendChar(c);
            【解决方案10】:

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

            1.字符串到 SecureString

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

            2。安全字符串到字符串

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

            这里是link

            【讨论】:

            • 这是迄今为止最常见用例的最佳解决方案!
            • 如果您的代码创建了一个具有您想要保护的值的string 对象,那么使用SecureString 是没有意义的。 SecureString 的目标是避免将字符串放在托管内存中,因此检查该内存的攻击者可以发现您想要隐藏的值。由于您正在调用的NetworkCredential 构造函数需要一个字符串,这不是要走的路...当然,您的代码是与 SecureString 相互转换的简单方法,但是使用它可以使您的代码像使用简单的string
            • @GianPaolo 虽然这在理论上是正确的,但实际上我们在使用某些库时仍然需要使用SecureString。老实说,如果有人正在积极扫描您的应用程序的内存,那么您已经迷路了。即使你正确使用了我从未见过的 SecureString,也会有其他有价值的数据需要提取。
            • @JonathanAllen:您可能是对的,但这仅仅是因为我们有一种将安全视为事后诸葛亮的文化。如果您正在创建一个食谱网站,朝鲜不太可能尝试扫描您的程序的内存。如果您正在编写银行软件,那么威胁就更加真实。
            • 非常有用的转换。当然,我正在使用 SecureString 作为密码构建安全软件,但为了与旧的或更简单的系统兼容,我们还需要支持常规字符串中的密码,否则我会被解雇 :-)
            【解决方案11】:

            以下 2 个扩展应该可以解决问题:

            1. 对于char 数组

              public static SecureString ToSecureString(this char[] _self)
              {
                  SecureString knox = new SecureString();
                  foreach (char c in _self)
                  {
                      knox.AppendChar(c);
                  }
                  return knox;
              }
              
            2. 对于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 DaggAppendChar 推荐。

            【讨论】:

              【解决方案12】:

              你可以使用这个简单的脚本

              private SecureString SecureStringConverter(string pass)
              {
                  SecureString ret = new SecureString();
              
                  foreach (char chr in pass.ToCharArray())
                      ret.AppendChar(chr);
              
                  return ret;
              }
              

              【讨论】:

              【解决方案13】:

              我只想向所有人指出,“这不是 SecureString 的意义所在”,许多提出这个问题的人可能在一个应用程序中,无论出于何种原因,无论是否合理,他们特别担心将密码的临时副本作为可 GC 的字符串放在堆上,但他们必须使用 接受 SecureString 的 API对象。所以,你有一个应用程序,你不关心密码是否在堆上,也许它仅供内部使用,密码只是因为底层网络协议需要它,而你发现存储密码的字符串不能用于例如设置远程 PowerShell 运行空间——但没有简单、直接的单行代码来创建您需要的 SecureString。这是一个小小的不便——但可能值得确保真正确实需要SecureString 的应用程序不会诱使作者使用System.StringSystem.Char[] 中介。 :-)

              【讨论】:

                【解决方案14】:

                为了完整起见,我添加了两个单元测试和方法来从 char 数组和字符串转换为 SecureString 并再次转换回来。您应该尝试完全避免使用字符串,并且只传递指向 char 数组的指针或 char 数组本身,就像我在此处提供的方法一样,因为字符串是不安全的,因为它们将数据以纯文本形式保存在托管内存中,数量相当不确定距离下一次 GC 运行或强制执行的时间,最好尽快将您的字符数组放入 SecureString 并保留在那里,并再次将其作为字符数组读回。

                测试如下所示:

                using NUnit.Framework;
                using System;
                using SecureStringExtensions;
                using System.Security;
                
                namespace SecureStringExtensions.Test
                {
                    [TestFixture]
                    public class SecureStringExtensionsTest
                    {
                        [Test]
                        [TestCase(new char[] { 'G', 'O', 'A', 'T', '1', '2', '3' })]
                        public void CopyCharArrayToSecureStringAndCopyBackToCharArrayReturnsExpected(char[] inputChars)
                        {
                            SecureString sec = inputChars.ToSecureString();
                            var copiedFromSec = sec.FromSecureStringToCharArray();
                            CollectionAssert.AreEqual(copiedFromSec, inputChars);                
                        }
                
                        [Test]
                        [TestCase("GOAT456")]
                        public void CopyStringToSecureStringAndCopyBackToUnsafeStringReturnsExpected(string inputString)
                        {
                            SecureString sec = inputString.ToSecureString();
                            var copiedFromSec = sec.FromSecureStringToUnsafeString();
                            Assert.AreEqual(copiedFromSec, inputString);
                        }
                    }
                }
                

                我们在这里有我们的扩展方法:

                using System;
                using System.Runtime.InteropServices;
                using System.Security;
                
                namespace SecureStringExtensions
                {
                    public static class SecureStringExtensions
                    {
                        public static SecureString ToSecureString(this string str)
                        {
                            return ToSecureString(str.ToCharArray());
                        }
                
                        public static SecureString ToSecureString(this char[] str)
                        {
                            var secureString = new SecureString();
                            Array.ForEach(str, secureString.AppendChar);
                            return secureString;
                        }
                
                        /// <summary>
                        /// Creates a managed character array from the secure string using methods in System.Runetime.InteropServices
                        /// copying data into a BSTR (unmanaged binary string) and then into a managed character array which is returned from this method.
                        /// Data in the unmanaged memory temporarily used are freed up before the method returns.
                        /// </summary>
                        /// <param name="secureString"></param>
                        /// <returns></returns>
                        public static char[] FromSecureStringToCharArray(this SecureString secureString)
                        {
                            char[] bytes;
                            var ptr = IntPtr.Zero;
                            try
                            {
                                //alloc unmanaged binary string  (BSTR) and copy contents of SecureString into this BSTR
                                ptr = Marshal.SecureStringToBSTR(secureString);
                                bytes = new char[secureString.Length];
                                //copy to managed memory char array from unmanaged memory 
                                Marshal.Copy(ptr, bytes, 0, secureString.Length);
                            }
                            finally
                            {
                                if (ptr != IntPtr.Zero)
                                {
                                    //free unmanaged memory
                                    Marshal.ZeroFreeBSTR(ptr);
                                }
                            }
                            return bytes;
                        }
                
                        /// <summary>
                        /// Returns an unsafe string in managed memory from SecureString. 
                        /// The use of this method is not recommended - use instead the <see cref="FromSecureStringToCharArray(SecureString)"/> method
                        /// as that method has not got multiple copies of data in managed memory like this method.
                        /// Data in unmanaged memory temporarily used are freed up before the method returns.
                        /// </summary>
                        /// <param name="secureString"></param>
                        /// <returns></returns>
                        public static string FromSecureStringToUnsafeString(this SecureString secureString)
                        {
                            if (secureString == null)
                            {
                                throw new ArgumentNullException(nameof(secureString));
                            }
                            var unmanagedString = IntPtr.Zero;
                            try
                            {
                                //copy secure string into unmanaged memory
                                unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(secureString);
                                //alloc managed string and copy contents of unmanaged string data into it
                                return Marshal.PtrToStringUni(unmanagedString);
                            }
                            finally
                            {
                                    if (unmanagedString != IntPtr.Zero)
                                {
                                    Marshal.FreeBSTR(unmanagedString);
                                }
                            }
                        }
                
                    }
                }
                

                在这里,我们使用 System.Runtime.InteropServices 中的方法来临时分配 BSTR(二进制非托管字符串),并确保释放临时使用的非托管内存。

                【讨论】:

                  猜你喜欢
                  • 2021-03-30
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-03-10
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多