【问题标题】:How to convert SecureString to System.String?如何将 SecureString 转换为 System.String?
【发布时间】:2010-10-23 13:16:38
【问题描述】:

所有关于通过创建 System.String 来解除 SecureString 安全的保留除了,怎么做?

如何将普通的 System.Security.SecureString 转换为 System.String?

我相信许多熟悉 SecureString 的人都会回答说,永远不应该将 SecureString 转换为普通的 .NET 字符串,因为它会删除所有安全保护。 我知道。但是现在我的程序无论如何都用普通字符串做所有事情,我正在尝试增强它的安全性,虽然我将使用一个向我返回 SecureString 的 API,但我试图用它来增加我的安全性。

我知道 Marshal.SecureStringToBSTR,但我不知道如何获取该 BSTR 并从中生成 System.String。

对于那些可能想知道我为什么要这样做的人,好吧,我从用户那里获取密码并将其作为 html 表单 POST 提交以将用户登录到网站。所以......这确实必须使用托管的未加密缓冲区来完成。如果我什至可以访问未管理的、未加密的缓冲区,我想我可以在网络流上逐字节地进行流写入,并希望整个过程都能保证密码的安全。我希望至少能找到其中一种情况的答案。

【问题讨论】:

    标签: c# .net security encryption


    【解决方案1】:

    使用System.Runtime.InteropServices.Marshal 类:

    String SecureStringToString(SecureString value) {
      IntPtr valuePtr = IntPtr.Zero;
      try {
        valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
        return Marshal.PtrToStringUni(valuePtr);
      } finally {
        Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
      }
    }
    

    如果您想避免创建托管字符串对象,可以使用Marshal.ReadInt16(IntPtr, Int32) 访问原始数据:

    void HandleSecureString(SecureString value) {
      IntPtr valuePtr = IntPtr.Zero;
      try {
        valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
        for (int i=0; i < value.Length; i++) {
          short unicodeChar = Marshal.ReadInt16(valuePtr, i*2);
          // handle unicodeChar
        }
      } finally {
        Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
      }
    }
    

    【讨论】:

    • 多年后也得到了我的支持,感谢您的帮助!只是一个简短的说明:这也可以作为一个静态的,在它自己的记忆中。
    • 我用StopWatchSecureStringToString 运行了4.6秒。对我来说太慢了。有人得到相同的时间或更快的时间吗?
    • @radbyx 在快速而肮脏的测试设置中,我可以在 76 毫秒内调用它 1000 次。第一次调用需要 0.3 毫秒,后续调用大约需要 0.07 毫秒。您的安全字符串有多大?您使用的是哪个版本的框架?
    • 我的secureString的长度是168。如果这回答了你的问题,我正在使用.NET Framework 3.5?我尝试了 5-10 次总是在 4.5-4.65 秒左右~我很想得到你的时间
    • 代码应使用SecureStringToBSTR,因为SecureString 可以包含\0 作为非终止字符,但SecureStringToGlobalAllocUnicode 将其视为以空值结尾的字符串。
    【解决方案2】:

    显然你知道这如何破坏了 SecureString 的全部目的,但我还是会重申它。

    如果你想要一个单行,试试这个:(仅限 .NET 4 及更高版本)

    string password = new System.Net.NetworkCredential(string.Empty, securePassword).Password;
    

    其中securePassword 是一个SecureString。

    【讨论】:

    • 虽然它确实违背了生产目的,但您的解决方案非常适合单元测试。谢谢。
    • 这帮助我弄清楚 SecureString (System.Security.SecureString) 没有被传递给我的 ApiController (webapi)。谢谢
    • 注意在 PowerShell 中这是[System.Net.NetworkCredential]::new('', $securePassword).Password
    • @TheIncorrigible1 你能详细说明一下吗?例如。 '' 何时与 [String]::Empty 不同类型? New-Object Net.Credential 也不适合我:找不到类型 [Net.Credential]:验证是否加载了包含此类型的程序集
    • 它违背了 SecureString 的目的,因为它将您的 SecureString 内容的非加密副本转换为普通字符串。每次您这样做时,您都会将未加密字符串的至少一个(并且可能是多个垃圾收集)副本添加到内存中。对于某些安全敏感的应用程序来说,这被认为是一种风险,因此专门实施了 SecureString 以降低风险。
    【解决方案3】:

    当。 发布后,我在this article 深处找到了答案。但是,如果有人知道如何访问此方法公开的 IntPtr 非托管、未加密缓冲区,一次一个字节,这样我就不必从中创建托管字符串对象来保持高安全性,请添加答案。 :)

    static String SecureStringToString(SecureString value)
    {
        IntPtr bstr = Marshal.SecureStringToBSTR(value);
    
        try
        {
            return Marshal.PtrToStringBSTR(bstr);
        }
        finally
        {
            Marshal.FreeBSTR(bstr);
        }
    }
    

    【讨论】:

    • 您当然可以使用unsafe 关键字和char*,只需调用bstr.ToPointer() 并进行转换。
    • @BenVoigt BSTR 为了安全起见,在字符串数据之后有一个空终止符,但也允许在字符串中嵌入空字符。所以它比这更复杂一些,您还需要检索位于该指针之前的长度前缀。 docs.microsoft.com/en-us/previous-versions/windows/desktop/…
    • @WimCoenen:没错但不重要。存储在 BSTR 中的长度将是 SecureString.Length 中已有长度的副本。
    • @BenVoigt 啊,我的错。我认为 SecureString 没有公开有关字符串的任何信息。
    • @WimCoenen: SecureString 不是试图隐藏该值,而是试图防止将该值的副本复制到无法可靠覆盖的区域,例如垃圾收集的内存、页面文件等. 目的是当SecureString 生命周期结束时,绝对不会在内存中保留任何秘密副本。它不会阻止您制作和泄露副本,但永远不会。
    【解决方案4】:

    在我看来,扩展方法是解决这个问题的最舒服的方法。

    我把Steve in CO'sexcellent answer放到一个扩展类中,如下所示,再加上我添加的第二种方法来支持另一个方向(字符串->安全字符串),所以你可以创建一个安全字符串然后将其转换为普通字符串:

    public static class Extensions
    {
        // convert a secure string into a normal plain text string
        public static String ToPlainString(this System.Security.SecureString secureStr)
        {
            String plainStr=new System.Net.NetworkCredential(string.Empty, secureStr).Password;
            return plainStr;
        }
    
        // convert a plain text string into a secure string
        public static System.Security.SecureString ToSecureString(this String plainStr)
        {
            var secStr = new System.Security.SecureString(); secStr.Clear();
            foreach (char c in plainStr.ToCharArray())
            {
                secStr.AppendChar(c);
            }
            return secStr;
        }
    }
    

    有了这个,您现在可以简单地来回转换字符串,如下所示:

    // create a secure string
    System.Security.SecureString securePassword = "MyCleverPwd123".ToSecureString(); 
    // convert it back to plain text
    String plainPassword = securePassword.ToPlainString();  // convert back to normal string
    

    但请记住,解码方法只能用于测试。

    【讨论】:

    • 我相信 ToSecureString 也应该只用于测试,因为 SecureString 的目的是防止字符串超出内存。
    • @ryanwebjackson - 是的。此外,这还不如真正的加密。没有使用密钥,因此每个人都可以显示明文。所以它不应该被命名为“安全”。 "security through obscurity" 的另一个例子。
    【解决方案5】:

    我认为最好 SecureString 依赖函数将它们的依赖逻辑封装在匿名函数中,以便更好地控制内存中的解密字符串(一旦固定)。

    在这个 sn-p 中解密 SecureStrings 的实现将:

    1. 将字符串固定在内存中(这是您想要做的,但这里的大多数答案似乎都缺少)。
    2. its reference 传递给 Func/Action 委托。
    3. 从内存中清除它并释放 finally 块中的 GC。

    与依赖不太理想的替代方案相比,这显然使“标准化”和维护调用者变得容易得多:

    • string DecryptSecureString(...) 帮助函数返回解密后的字符串。
    • 在需要的地方复制此代码。

    请注意,您有两种选择:

    1. static T DecryptSecureString&lt;T&gt; 允许您从调用者访问Func 委托的结果(如DecryptSecureStringWithFunc 测试方法中所示)。
    2. static void DecryptSecureString 只是一个“无效”版本,在您实际上不想/不需要返回任何东西的情况下使用 Action 委托(如 DecryptSecureStringWithAction 测试方法中所示)。

    两者的示例用法可以在包含的StringsTest 类中找到。

    Strings.cs

    using System;
    using System.Runtime.InteropServices;
    using System.Security;
    
    namespace SecurityUtils
    {
        public partial class Strings
        {
            /// <summary>
            /// Passes decrypted password String pinned in memory to Func delegate scrubbed on return.
            /// </summary>
            /// <typeparam name="T">Generic type returned by Func delegate</typeparam>
            /// <param name="action">Func delegate which will receive the decrypted password pinned in memory as a String object</param>
            /// <returns>Result of Func delegate</returns>
            public static T DecryptSecureString<T>(SecureString secureString, Func<string, T> action)
            {
                var insecureStringPointer = IntPtr.Zero;
                var insecureString = String.Empty;
                var gcHandler = GCHandle.Alloc(insecureString, GCHandleType.Pinned);
    
                try
                {
                    insecureStringPointer = Marshal.SecureStringToGlobalAllocUnicode(secureString);
                    insecureString = Marshal.PtrToStringUni(insecureStringPointer);
    
                    return action(insecureString);
                }
                finally
                {
                    //clear memory immediately - don't wait for garbage collector
                    fixed(char* ptr = insecureString )
                    {
                        for(int i = 0; i < insecureString.Length; i++)
                        {
                            ptr[i] = '\0';
                        }
                    }
    
                    insecureString = null;
    
                    gcHandler.Free();
                    Marshal.ZeroFreeGlobalAllocUnicode(insecureStringPointer);
                }
            }
    
            /// <summary>
            /// Runs DecryptSecureString with support for Action to leverage void return type
            /// </summary>
            /// <param name="secureString"></param>
            /// <param name="action"></param>
            public static void DecryptSecureString(SecureString secureString, Action<string> action)
            {
                DecryptSecureString<int>(secureString, (s) =>
                {
                    action(s);
                    return 0;
                });
            }
        }
    }
    

    StringsTest.cs

    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System.Security;
    
    namespace SecurityUtils.Test
    {
        [TestClass]
        public class StringsTest
        {
            [TestMethod]
            public void DecryptSecureStringWithFunc()
            {
                // Arrange
                var secureString = new SecureString();
    
                foreach (var c in "UserPassword123".ToCharArray())
                    secureString.AppendChar(c);
    
                secureString.MakeReadOnly();
    
                // Act
                var result = Strings.DecryptSecureString<bool>(secureString, (password) =>
                {
                    return password.Equals("UserPassword123");
                });
    
                // Assert
                Assert.IsTrue(result);
            }
    
            [TestMethod]
            public void DecryptSecureStringWithAction()
            {
                // Arrange
                var secureString = new SecureString();
    
                foreach (var c in "UserPassword123".ToCharArray())
                    secureString.AppendChar(c);
    
                secureString.MakeReadOnly();
    
                // Act
                var result = false;
    
                Strings.DecryptSecureString(secureString, (password) =>
                {
                    result = password.Equals("UserPassword123");
                });
    
                // Assert
                Assert.IsTrue(result);
            }
        }
    }
    

    显然,这并不能防止以下列方式滥用此功能,因此请注意不要这样做:

    [TestMethod]
    public void DecryptSecureStringWithAction()
    {
        // Arrange
        var secureString = new SecureString();
    
        foreach (var c in "UserPassword123".ToCharArray())
            secureString.AppendChar(c);
    
        secureString.MakeReadOnly();
    
        // Act
        string copyPassword = null;
    
        Strings.DecryptSecureString(secureString, (password) =>
        {
            copyPassword = password; // Please don't do this!
        });
    
        // Assert
        Assert.IsNull(copyPassword); // Fails
    }
    

    编码愉快!

    【讨论】:

    • 为什么不使用Marshal.Copy(new byte[insecureString.Length], 0, insecureStringPointer, (int)insecureString.Length); 而不是fixed 部分?
    • @sclarke81,好主意,但您需要使用[char],而不是[byte]
    • 整体方法很有希望,但我不认为您尝试固定包含不安全(纯文本)副本的托管字符串是有效的:您要固定的是 original 已初始化为String.Empty 的字符串对象,而不是Marshal.PtrToStringUni() 创建并返回的新分配实例。
    【解决方案6】:

    我基于answer from rdev5 创建了以下扩展方法。固定托管字符串很重要,因为它可以防止垃圾收集器移动它并留下您无法擦除的副本。

    我认为我的解决方案的优点是不需要不安全的代码。

    /// <summary>
    /// Allows a decrypted secure string to be used whilst minimising the exposure of the
    /// unencrypted string.
    /// </summary>
    /// <typeparam name="T">Generic type returned by Func delegate.</typeparam>
    /// <param name="secureString">The string to decrypt.</param>
    /// <param name="action">
    /// Func delegate which will receive the decrypted password as a string object
    /// </param>
    /// <returns>Result of Func delegate</returns>
    /// <remarks>
    /// This method creates an empty managed string and pins it so that the garbage collector
    /// cannot move it around and create copies. An unmanaged copy of the the secure string is
    /// then created and copied into the managed string. The action is then called using the
    /// managed string. Both the managed and unmanaged strings are then zeroed to erase their
    /// contents. The managed string is unpinned so that the garbage collector can resume normal
    /// behaviour and the unmanaged string is freed.
    /// </remarks>
    public static T UseDecryptedSecureString<T>(this SecureString secureString, Func<string, T> action)
    {
        int length = secureString.Length;
        IntPtr sourceStringPointer = IntPtr.Zero;
    
        // Create an empty string of the correct size and pin it so that the GC can't move it around.
        string insecureString = new string('\0', length);
        var insecureStringHandler = GCHandle.Alloc(insecureString, GCHandleType.Pinned);
    
        IntPtr insecureStringPointer = insecureStringHandler.AddrOfPinnedObject();
    
        try
        {
            // Create an unmanaged copy of the secure string.
            sourceStringPointer = Marshal.SecureStringToBSTR(secureString);
    
            // Use the pointers to copy from the unmanaged to managed string.
            for (int i = 0; i < secureString.Length; i++)
            {
                short unicodeChar = Marshal.ReadInt16(sourceStringPointer, i * 2);
                Marshal.WriteInt16(insecureStringPointer, i * 2, unicodeChar);
            }
    
            return action(insecureString);
        }
        finally
        {
            // Zero the managed string so that the string is erased. Then unpin it to allow the
            // GC to take over.
            Marshal.Copy(new byte[length], 0, insecureStringPointer, length);
            insecureStringHandler.Free();
    
            // Zero and free the unmanaged string.
            Marshal.ZeroFreeBSTR(sourceStringPointer);
        }
    }
    
    /// <summary>
    /// Allows a decrypted secure string to be used whilst minimising the exposure of the
    /// unencrypted string.
    /// </summary>
    /// <param name="secureString">The string to decrypt.</param>
    /// <param name="action">
    /// Func delegate which will receive the decrypted password as a string object
    /// </param>
    /// <returns>Result of Func delegate</returns>
    /// <remarks>
    /// This method creates an empty managed string and pins it so that the garbage collector
    /// cannot move it around and create copies. An unmanaged copy of the the secure string is
    /// then created and copied into the managed string. The action is then called using the
    /// managed string. Both the managed and unmanaged strings are then zeroed to erase their
    /// contents. The managed string is unpinned so that the garbage collector can resume normal
    /// behaviour and the unmanaged string is freed.
    /// </remarks>
    public static void UseDecryptedSecureString(this SecureString secureString, Action<string> action)
    {
        UseDecryptedSecureString(secureString, (s) =>
        {
            action(s);
            return 0;
        });
    }
    

    【讨论】:

    • 虽然您的代码不会泄露字符串的副本,但它仍然代表pit of despair。几乎对System.String 对象的每个操作都会生成未固定和未擦除的副本。这就是为什么 SecureString 没有内置它。
    • 很好,不过要将整个字符串归零,您必须使用new char[length](或将lengthsizeof(char) 相乘)。
    • @BenVoigt:只要action 委托不创建临时、固定、然后归零的字符串的副本,这种方法应该与SecureString 本身一样安全或不安全- 要使用后者,也必须在某些时候创建纯文本表示,因为安全字符串不是操作系统级别的构造;相对安全性来自控制该字符串的生命周期并确保它在使用后被擦除。
    • @mklement0: SecureString 没有成员函数和重载运算符,可以在所有地方进行复制。 System.String 确实如此。
    • @mklement0:考虑到它将它传递给确实接受SecureStringNetworkCredential 构造函数,这非常荒谬。
    【解决方案7】:

    根据 sclarke81 解决方案和 John Flaherty 修复的最终工作解决方案是:

        public static class Utils
        {
            /// <remarks>
            /// This method creates an empty managed string and pins it so that the garbage collector
            /// cannot move it around and create copies. An unmanaged copy of the the secure string is
            /// then created and copied into the managed string. The action is then called using the
            /// managed string. Both the managed and unmanaged strings are then zeroed to erase their
            /// contents. The managed string is unpinned so that the garbage collector can resume normal
            /// behaviour and the unmanaged string is freed.
            /// </remarks>
            public static T UseDecryptedSecureString<T>(this SecureString secureString, Func<string, T> action)
            {
                int length = secureString.Length;
                IntPtr sourceStringPointer = IntPtr.Zero;
    
                // Create an empty string of the correct size and pin it so that the GC can't move it around.
                string insecureString = new string('\0', length);
                var insecureStringHandler = GCHandle.Alloc(insecureString, GCHandleType.Pinned);
    
                IntPtr insecureStringPointer = insecureStringHandler.AddrOfPinnedObject();
    
                try
                {
                    // Create an unmanaged copy of the secure string.
                    sourceStringPointer = Marshal.SecureStringToBSTR(secureString);
    
                    // Use the pointers to copy from the unmanaged to managed string.
                    for (int i = 0; i < secureString.Length; i++)
                    {
                        short unicodeChar = Marshal.ReadInt16(sourceStringPointer, i * 2);
                        Marshal.WriteInt16(insecureStringPointer, i * 2, unicodeChar);
                    }
    
                    return action(insecureString);
                }
                finally
                {
                    // Zero the managed string so that the string is erased. Then unpin it to allow the
                    // GC to take over.
                    Marshal.Copy(new byte[length * 2], 0, insecureStringPointer, length * 2);
                    insecureStringHandler.Free();
    
                    // Zero and free the unmanaged string.
                    Marshal.ZeroFreeBSTR(sourceStringPointer);
                }
            }
    
            /// <summary>
            /// Allows a decrypted secure string to be used whilst minimising the exposure of the
            /// unencrypted string.
            /// </summary>
            /// <param name="secureString">The string to decrypt.</param>
            /// <param name="action">
            /// Func delegate which will receive the decrypted password as a string object
            /// </param>
            /// <returns>Result of Func delegate</returns>
            /// <remarks>
            /// This method creates an empty managed string and pins it so that the garbage collector
            /// cannot move it around and create copies. An unmanaged copy of the the secure string is
            /// then created and copied into the managed string. The action is then called using the
            /// managed string. Both the managed and unmanaged strings are then zeroed to erase their
            /// contents. The managed string is unpinned so that the garbage collector can resume normal
            /// behaviour and the unmanaged string is freed.
            /// </remarks>
            public static void UseDecryptedSecureString(this SecureString secureString, Action<string> action)
            {
                UseDecryptedSecureString(secureString, (s) =>
                {
                    action(s);
                    return 0;
                });
            }
        }
    

    【讨论】:

      【解决方案8】:

      我来自This answer by sclarke81。我喜欢他的回答,我正在使用衍生物,但 sclarke81 有一个错误。我没有名气,所以无法评论。这个问题似乎足够小,不需要另一个答案,我可以编辑它。所以我做了。它被拒绝了。所以现在我们有了另一个答案。

      sclarke81 我希望你看到这个(最后):

      Marshal.Copy(new byte[length], 0, insecureStringPointer, length);
      

      应该是:

      Marshal.Copy(new byte[length * 2], 0, insecureStringPointer, length * 2);
      

      以及修复错误的完整答案:

      
          /// 
          /// Allows a decrypted secure string to be used whilst minimising the exposure of the
          /// unencrypted string.
          /// 
          /// Generic type returned by Func delegate.
          /// The string to decrypt.
          /// 
          /// Func delegate which will receive the decrypted password as a string object
          /// 
          /// Result of Func delegate
          /// 
          /// This method creates an empty managed string and pins it so that the garbage collector
          /// cannot move it around and create copies. An unmanaged copy of the the secure string is
          /// then created and copied into the managed string. The action is then called using the
          /// managed string. Both the managed and unmanaged strings are then zeroed to erase their
          /// contents. The managed string is unpinned so that the garbage collector can resume normal
          /// behaviour and the unmanaged string is freed.
          /// 
          public static T UseDecryptedSecureString(this SecureString secureString, Func action)
          {
              int length = secureString.Length;
              IntPtr sourceStringPointer = IntPtr.Zero;
      
              // Create an empty string of the correct size and pin it so that the GC can't move it around.
              string insecureString = new string('\0', length);
              var insecureStringHandler = GCHandle.Alloc(insecureString, GCHandleType.Pinned);
      
              IntPtr insecureStringPointer = insecureStringHandler.AddrOfPinnedObject();
      
              try
              {
                  // Create an unmanaged copy of the secure string.
                  sourceStringPointer = Marshal.SecureStringToBSTR(secureString);
      
                  // Use the pointers to copy from the unmanaged to managed string.
                  for (int i = 0; i < secureString.Length; i++)
                  {
                      short unicodeChar = Marshal.ReadInt16(sourceStringPointer, i * 2);
                      Marshal.WriteInt16(insecureStringPointer, i * 2, unicodeChar);
                  }
      
                  return action(insecureString);
              }
              finally
              {
                  // Zero the managed string so that the string is erased. Then unpin it to allow the
                  // GC to take over.
                  Marshal.Copy(new byte[length * 2], 0, insecureStringPointer, length * 2);
                  insecureStringHandler.Free();
      
                  // Zero and free the unmanaged string.
                  Marshal.ZeroFreeBSTR(sourceStringPointer);
              }
          }
      
          /// 
          /// Allows a decrypted secure string to be used whilst minimising the exposure of the
          /// unencrypted string.
          /// 
          /// The string to decrypt.
          /// 
          /// Func delegate which will receive the decrypted password as a string object
          /// 
          /// Result of Func delegate
          /// 
          /// This method creates an empty managed string and pins it so that the garbage collector
          /// cannot move it around and create copies. An unmanaged copy of the the secure string is
          /// then created and copied into the managed string. The action is then called using the
          /// managed string. Both the managed and unmanaged strings are then zeroed to erase their
          /// contents. The managed string is unpinned so that the garbage collector can resume normal
          /// behaviour and the unmanaged string is freed.
          /// 
          public static void UseDecryptedSecureString(this SecureString secureString, Action action)
          {
              UseDecryptedSecureString(secureString, (s) =>
              {
                  action(s);
                  return 0;
              });
          }
      }
      

      【讨论】:

      • 好点;我对引用的答案发表了评论,应该通知 OP。
      【解决方案9】:

      这个 C# 代码就是你想要的。

      %ProjectPath%/SecureStringsEasy.cs

      using System;
      using System.Security;
      using System.Runtime.InteropServices;
      namespace SecureStringsEasy
      {
          public static class MyExtensions
          {
              public static SecureString ToSecureString(string input)
              {
                  SecureString secureString = new SecureString();
                  foreach (var item in input)
                  {
                      secureString.AppendChar(item);
                  }
                  return secureString;
              }
              public static string ToNormalString(SecureString input)
              {
                  IntPtr strptr = Marshal.SecureStringToBSTR(input);
                  string normal = Marshal.PtrToStringBSTR(strptr);
                  Marshal.ZeroFreeBSTR(strptr);
                  return normal;
              }
          }
      }
      

      【讨论】:

      • 我不确定你是否打算这样做,但参数需要以“this”为前缀才能成为正确的扩展方法
      【解决方案10】:

      作为答案接受的代码是正确的,并且在大多数情况下都可以使用,但是正如 cmets 中提到的那样,使用 BSTR 会更好,并且可以涵盖所有情况:

      private string SecureStringToString(SecureString value) {
          IntPtr valuePtr = IntPtr.Zero;
          try {
              valuePtr = Marshal.SecureStringToBSTR(value);
              return Marshal.PtrToStringBSTR(valuePtr);
          } finally {
              Marshal.ZeroFreeBSTR(valuePtr);
          }
      }
      

      【讨论】:

        【解决方案11】:

        使用以下内容:

        var plaintextPwd = new System.Net.NetworkCredential("", <securestring with your encrypted password>).Password
        

        【讨论】:

          【解决方案12】:
          // using so that Marshal doesn't have to be qualified
          using System.Runtime.InteropServices;    
          //using for SecureString
          using System.Security;
          public string DecodeSecureString (SecureString Convert) 
          {
              //convert to IntPtr using Marshal
              IntPtr cvttmpst = Marshal.SecureStringToBSTR(Convert);
              //convert to string using Marshal
              string cvtPlainPassword = Marshal.PtrToStringAuto(cvttmpst);
              //return the now plain string
              return cvtPlainPassword;
          }
          

          【讨论】:

          • 这个答案有内存泄漏。
          • @BenVoigt 您能否进一步解释一下这是如何导致内存泄漏的?
          • @ElRonnoco:没有什么能明确释放BSTR,它不是.NET 对象,所以垃圾收集器也不会处理它。与 5 年前发布的 stackoverflow.com/a/818709/103167 相比,没有泄露。
          • 此答案不适用于非 Windows 平台。 PtrToStringAuto 是错误的解释见:github.com/PowerShell/PowerShell/issues/…
          【解决方案13】:

          如果您使用StringBuilder 而不是string,您可以在完成后覆盖内存中的实际值。这样密码就不会在内存中徘徊,直到垃圾收集器拾取它。

          StringBuilder.Append(plainTextPassword);
          StringBuilder.Clear();
          // overwrite with reasonably random characters
          StringBuilder.Append(New Guid().ToString());
          

          【讨论】:

          • 虽然这是真的,垃圾收集器仍然可能在分代压缩期间在内存中移动 StringBuilder 缓冲区,这使得“覆盖实际值”失败,因为还有另一个(或更多)剩余副本没有被破坏。
          • 这甚至不能远程回答问题。
          猜你喜欢
          • 2019-12-07
          • 1970-01-01
          • 1970-01-01
          • 2010-10-14
          • 1970-01-01
          • 1970-01-01
          • 2012-09-07
          • 2015-07-09
          • 1970-01-01
          相关资源
          最近更新 更多