[C#] 如何將SecureString轉換為System.String?


Answers

顯然你知道這是如何破壞SecureString的全部目的,但我會重申它。

如果你想要一行代碼,試試這個:(僅限.NET 4及以上版本)

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

securePassword是SecureString。

Question

所有關於通過創建一個System.String來解除SecureString的保留 ,如何完成?

如何將普通的System.Security.SecureString轉換為System.String?

我相信很多熟悉SecureString的人都會回應說,不應該將SecureString轉換為普通的.NET字符串,因為它會刪除所有安全保護。 我知道 。 但是現在我的程序用普通的字符串來處理所有事情,而且我正在嘗試增強其安全性,儘管我將使用一個返回SecureString的API,但我並不想使用它來提高安全性。

我知道Marshal.SecureStringToBSTR,但我不知道如何接受這個BSTR並將System.String放出去。

對於那些可能會要求知道為什麼我會想要這樣做的人,那麼,我正在從用戶那裡獲取一個密碼,並將其提交為HTML表單POST,以便將用戶登錄到網站。 所以......這確實需要使用託管的,未加密的緩衝區來完成。 如果我甚至可以訪問非託管的,未加密的緩衝區,我想我可以在網絡流上逐字節地寫流,並希望能夠保證整個密碼的安全。 我希望能夠回答至少其中一種情況。




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



我認為最好是依賴於SecureString函數其依賴邏輯封裝在一個匿名函數中,以便更好地控制內存中的解密字符串(一旦被鎖定)。

在這個片段中解密SecureStrings的實現將會:

  1. 將字符串固定在內存中(這是您想要做的,但似乎從大多數答案中都沒有)。
  2. 其引用傳遞給Func / Action委託。
  3. 從內存中擦洗並釋放finally塊中的GC。

這顯然使“標準化”和維護呼叫者比依靠不太理想的替代方案更容易:

  • string DecryptSecureString(...)輔助函數string DecryptSecureString(...)返回解密後的string DecryptSecureString(...)
  • 在需要的地方復制此代碼。

注意這裡,你有兩個選擇:

  1. static T DecryptSecureString<T> ,它允許您從調用方訪問Func委託的結果(如DecryptSecureStringWithFunc測試方法所示)。
  2. static void DecryptSecureString只是一個“void”版本,在實際上不需要/不需要返回任何東西的情況下(如DecryptSecureStringWithAction測試方法中所演示的),它使用Action委託。

包含的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
            {
                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
}

快樂的編碼!