Як конвертувати SecureString в System.String?


156

Усі застереження щодо незахищеності вашого SecureString, створивши System.String з нього вбік , як це зробити?

Як я можу перетворити звичайний System.Security.SecureString в System.String?

Я впевнений, що багато хто з вас, хто знайомий із SecureString, збираються відповісти, що ніколи не слід перетворювати SecureString у звичайну .NET-рядок, оскільки це знімає всі засоби захисту. Я знаю . Але зараз моя програма все одно робить звичайні рядки, і я намагаюся підвищити її безпеку, і хоча я буду використовувати API, який повертає SecureString до мене, я не намагаюся використовувати це для підвищення своєї безпеки.

Я знаю про Marshal.SecureStringToBSTR, але я не знаю, як взяти BSTR і зробити System.String з нього.

Для тих, хто може вимагати знати, чому я коли-небудь хотів би це зробити, ну я беру пароль від користувача і подаю його як HTML-форму POST для входу користувача на веб-сайт. Отже ... це дійсно потрібно робити з керованими, незашифрованими буферами. Якби я міг навіть отримати доступ до некерованого, незашифрованого буфера, я думаю, що я міг би робити байт-байт-потік потоку в мережевому потоці і сподіваюся, що це захищає пароль увесь шлях. Я сподіваюся на відповідь хоча б на один із цих сценаріїв.

Відповіді:


192

Використовуйте 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);
  }
}

1
Я також отримав своє голосування навіть через роки, дякую за допомогу! Лише швидка примітка: це також працює як статичний, у власній пам'яті.
Джон Костюм

Я використовував StopWatchі SecureStringToStringвзяв 4,6 сек для запуску. Мені це повільно. Хтось отримує той же час чи щось швидше?
radbyx

@radbyx У швидкій та брудній тестовій установці я можу викликати це 1000 разів за 76 мс. Перша виклик займає 0,3 мс, а наступні виклики - 0,07 мс. Наскільки велика ваша захисна рядок і яку версію фреймворку ви використовуєте?
Расмус Фабер

Довжина мого secureString становить 168. Я використовую .NET Framework 3.5, якщо це відповів на ваше запитання? Я пробував 5-10 разів, це завжди 4,5-4,65 сек ~ я б хотів отримати ваш час
radbyx

@RasmusFaber Мій поганий, я додав Database.GetConnectionString()у свій код, щоб отримати мій secureString, який був злим складом, який зайняв майже 5 секунд (і так, я мушу розглянути це! :) Ваш код зайняв у моїй секундомірі 10000 секунд, так що це все добре. Дякую, що вказали на мене в правильному напрямку.
radbyx

108

Очевидно, ви знаєте, як це перемагає цілі цілі SecureString, але я все одно відновлю це.

Якщо ви хочете однолінійний, спробуйте: (.NET 4 і вище)

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

Де securePassword - це SecureString.


10
Хоча це і перемагає мету у виробництві, ваше рішення ідеально підходить для одиничних тестів. Дякую.
beterthanlife

Це допомогло мені зрозуміти, що SecureString (System.Security.SecureString) не передається моєму ApiController (webapi). Thx
granadaCoder

5
Зауважте, що в PowerShell це[System.Net.NetworkCredential]::new('', $securePassword).Password
вдень

1
@ TheIncorrigible1 ви можете розробити? Наприклад, коли це ''не той самий тип, як [String]::Empty? Також New-Object Net.Credentialне працює для мене: Неможливо знайти тип [Net.Credential]: перевірте, що збірка, що містить цей тип, завантажена
stijn

2
Він перемагає призначення SecureString, оскільки він робить незашифровану копію вмісту SecureString у звичайний рядок. Кожен раз, коли ви робите це, ви додаєте до пам'яті принаймні одну (і разом зі збиранням сміття, можливо, декілька) копій незашифрованого рядка. Це вважається ризиком для деяких застосунків, захищених від безпеки, і SecureString був реалізований спеціально для зниження ризику.
Стів В СО

49

Данг. відразу після публікації цього я знайшов відповідь глибоко в цій статті . Але якщо хтось знає, як отримати доступ до некерованого буфера IntPtr, незашифрованого буфера, який піддається цьому методу, по одному байту, так що мені не доведеться створювати з нього керований рядковий об'єкт, щоб зберегти високу безпеку, будь ласка, додайте відповідь. :)

static String SecureStringToString(SecureString value)
{
    IntPtr bstr = Marshal.SecureStringToBSTR(value);

    try
    {
        return Marshal.PtrToStringBSTR(bstr);
    }
    finally
    {
        Marshal.FreeBSTR(bstr);
    }
}

Ви, звичайно, можете скористатися unsafeключовим словом і a char*, просто зателефонуйте bstr.ToPointer()та передавайте.
Бен Войт

@BenVoigt BSTR має нульовий термінатор після рядкових даних для безпеки, але також дозволяє нульові символи, вбудовані в рядок. Отже, це трохи складніше, ніж це, вам також потрібно отримати префікс довжини, що знаходиться перед цим вказівником. docs.microsoft.com/en-us/previous-versions/windows/desktop/…
Wim

@WimCoenen: Це правда, але неважливо. Довжина, що зберігається в BSTR, буде копією довжини, яка вже доступна SecureString.Length.
Ben Voigt

@BenVoigt ах, мій поганий. Я думав, що SecureString не виставляє жодної інформації про рядок.
Вім

@WimCoenen: SecureStringне намагається приховати значення, він намагається запобігти перетворенню копій значення в регіони, які неможливо надійно перезаписати, наприклад, зібрана сміттям пам’ять, файл сторінки тощо. Намір полягає в тому, що коли SecureStringжиття закінчиться, абсолютно жодна копія секрету не залишається в пам'яті. Це не заважає вам робити та просочувати копію, але це ніколи не відбувається.
Ben Voigt

15

На мою думку, методи розширення - це найзручніший спосіб вирішити це.

Я взяв Стіва у відмінній відповіді CO і переніс його в клас розширення наступним чином, а також другий метод, який я додав, щоб підтримувати інший напрямок (рядок -> безпечний рядок), так що ви можете створити захищену рядок і перетворити її в звичайний рядок після цього:

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

Але майте на увазі, метод декодування повинен використовуватися лише для тестування.


14

Я думаю, що найкраще для SecureStringзалежних функцій інкапсулювати свою залежну логіку в анонімну функцію для кращого контролю над розшифрованим рядком у пам'яті (один раз зафіксованим).

Реалізація для розшифрування SecureStrings у цьому фрагменті:

  1. Закріпіть рядок у пам’яті (це те, що ви хочете зробити, але, здається, відсутнє в більшості відповідей тут).
  2. Передайте посилання на делегата Func / Action.
  3. Вичистіть його з пам'яті та відпустіть GC у finallyблоці.

Це, очевидно, значно полегшує "стандартизацію" та підтримку абонентів, посилаючись на менш бажані альтернативи:

  • Повернення розшифрованого рядка з string DecryptSecureString(...)функції помічника.
  • Дублювання цього коду, де це потрібно.

Зауважте тут, у вас є два варіанти:

  1. static T DecryptSecureString<T>що дозволяє отримати доступ до результату 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

@ sclarke81, хороша ідея, але вам потрібно скористатися [char], не варто [byte].
mklement0

1
Загальний підхід є багатообіцяючим, але я не думаю, що ваша спроба закріпити керовану рядок, що містить незахищену (звичайний текст) копію, є ефективною: те, що ви закріплюєте натомість, є оригінальним об’єктом рядка, який ви ініціалізували String.Empty, не новостворений екземпляр, створений та повернутий Marshal.PtrToStringUni().
mklement0

7

Я створив такі методи розширення на основі відповіді від 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;
    });
}

Хоча ваш код не просочується копією рядка, він все ще є ямою відчаю . Практично кожна операція на System.Stringоб’єкті буде робити незакріплені та неочищені копії. Ось чому це не вбудовано SecureString.
Ben Voigt

Приємно, хоч для нуля всієї нитки вам доведеться використовувати new char[length](або множити lengthна sizeof(char)).
mklement0

@BenVoigt: Поки actionделегат не створює копії тимчасової, закріпленої, а потім зануленої нуль рядки, цей підхід повинен бути настільки ж безпечним або небезпечним, як SecureStringсам - для використання останнього, звичайне текстове подання теж повинне створюватись в якийсь момент, враховуючи, що захисні рядки не є конструкціями на рівні ОС; відносна безпека виходить з контролю за терміном експлуатації цієї струни та забезпечення її стирання після використання.
mklement0

@ mklement0: SecureStringне має функцій членів і перевантажених операторів, які роблять копії повсюдно . System.Stringробить.
Бен Войгт

1
@ mklement0: Що досить нерозумно, враховуючи, що він передає його NetworkCredentialконструктору, який НЕ приймає SecureString.
Бен Войгт

0

Цей код 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;
        }
    }
}

0

Я отримав цю відповідь від 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;
        });
    }
}

Гарна думка; Я залишив коментар до посилається на відповідь, який повинен повідомити ОП.
mklement0

0

Остаточним робочим рішенням згідно з рішенням sclarke81 та виправлень Джона Флаерті є:

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

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

Ця відповідь має витік пам'яті.
Ben Voigt

@BenVoigt Чи можете ви пояснити, будь ласка, як це має витік пам'яті?
Ель Ронноко

4
@ElRonnoco: Ніщо не звільняє BSTRявно, і це не .NET-об’єкт, тому збирач сміття також не піклується про це. Порівняйте з stackoverflow.com/a/818709/103167, який був розміщений на 5 років раніше і не протікає.
Бен Войгт

Ця відповідь не працює на інших платформах Windows. PtrToStringAuto неправильно пояснює див. Див .: github.com/PowerShell/PowerShell/isissue/…
К. Френк

-5

Якщо ви використовуєте StringBuilderзамість string, ви можете перезаписати фактичне значення в пам'ять, коли закінчите. Таким чином, пароль не буде зависати в пам'яті, поки сміття не підбере його.

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

2
Хоча це правда, збирач сміття все ще може переміщувати буфер StringBuilder навколо в пам'яті під час ущільнення поколінь, що призводить до того, що "перезаписати фактичне значення" не вдасться, оскільки є інша (або більше) копія, що залишилася, що не знищується.
Бен Войгт

4
Це навіть не віддалено відповідає на питання.
Джей Салліван
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.