Как преобразовать SecureString в System.String?

156

Все оговорки о unsecuring вашего 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 секунд (и да, я должен посмотреть на это! :) Ваш код занял 0,00 мили секунды в моем секундомере, так что это все хорошо. Спасибо, что указал мне правильное направление.
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
stijn
1
@ TheIncorrigible1 вы можете уточнить? Например, когда это ''не тот же тип, что и [String]::Empty? Также New-Object Net.Credentialне работает для меня: не удается найти тип [Net.Credential]: убедитесь, что сборка, содержащая этот тип загружен
stijn
2
Он отрицает цель SecureString, поскольку делает незашифрованную копию содержимого SecureString обычной строкой. Каждый раз, когда вы делаете это, вы добавляете в память по крайней мере одну (и с помощью «Сборщика мусора», возможно, несколько) копий вашей незашифрованной строки. Это считается риском для некоторых чувствительных к безопасности приложений, и SecureString была реализована специально для снижения риска.
Стив Ин CO
49

Dang. сразу после публикации я нашел ответ глубоко в этой статье . Но если кто-то знает, как получить доступ к неуправляемому, незашифрованному буферу 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/…
Вим Коенен,
@WimCoenen: правда, но неважно. Длина, сохраненная в BSTR, будет копией длины, уже доступной из SecureString.Length.
Бен Фойгт
@ BenVoigt ах, мой плохой. Я думал, что SecureString не предоставляет никакой информации о строке.
Вим Коенен
@WimCoenen: SecureStringне пытается скрыть значение, он пытается предотвратить копирование значения в регионы, которые невозможно перезаписать, например, память для сбора мусора, файл подкачки и т. Д. Смысл в том, что по окончании времени SecureStringжизни абсолютно копия секрета не остается в памяти. Это не мешает вам делать и передавать копию, но никогда не делает.
Бен Фойгт
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

Но имейте в виду, что метод декодирования должен использоваться только для тестирования.

Matt
источник
14

Я думаю, что для SecureStringзависимых функций было бы лучше инкапсулировать свою зависимую логику в анонимную функцию для лучшего контроля за расшифрованной строкой в ​​памяти (когда-то закрепленной).

Реализация для расшифровки SecureStrings в этом фрагменте будет:

  1. Закрепите строку в памяти (это то, что вы хотите сделать, но, похоже, отсутствует в большинстве ответов здесь).
  2. Передайте свою ссылку делегату Func / Action.
  3. Вычистите это из памяти и освободите GC в finallyблоке.

Это, очевидно, значительно упрощает «стандартизацию» и поддержку вызывающих абонентов по сравнению с использованием менее желательных альтернатив:

  • Возвращение расшифрованной строки из string DecryptSecureString(...)вспомогательной функции.
  • Дублирование этого кода везде, где это необходимо.

Обратите внимание, у вас есть два варианта:

  1. static T DecryptSecureString<T>который позволяет получить доступ к результату Funcделегата от вызывающей стороны (как показано в DecryptSecureStringWithFuncметоде test).
  2. static void DecryptSecureStringэто просто «пустая» версия, которая использует Actionделегата в тех случаях, когда вы на самом деле не хотите / не должны ничего возвращать (как продемонстрировано в DecryptSecureStringWithActionметоде test).

Пример использования для обоих можно найти в 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;
    });
}
sclarke81
источник
Хотя ваш код не пропускает копию строки, он по-прежнему представляет собой яму отчаяния . Практически каждая операция над System.Stringобъектом будет создавать не закрепленные и неуничтоженные копии. Вот почему это не встроено SecureString.
Бен Фойгт
Хорошо, хотя для обнуления всей строки вам придется использовать new char[length](или умножить lengthна sizeof(char)).
mklement0
@BenVoigt: до тех пор, пока actionделегат не создает копии временной, закрепленной, а затем обнуляемой строки, этот подход должен быть таким же безопасным, как и SecureStringсам по себе, - чтобы использовать последнюю, текстовое представление также должно быть созданным в какой-то момент, учитывая, что защищенные строки не являются конструкциями уровня ОС; Относительная безопасность достигается за счет контроля времени жизни этой строки и обеспечения ее удаления после использования.
mklement0
@ mklement0: SecureStringне имеет функций-членов и перегруженных операторов, которые делают копии повсюду. System.Stringделает.
Бен Фойгт
1
@ mklement0: Что чертовски абсурдно, учитывая, что он передает его NetworkCredentialконструктору, который принимает a 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;
}
Джесси Мотес
источник
Этот ответ имеет утечку памяти.
Бен Фойгт
@ BenVoigt Можете ли вы объяснить, пожалуйста, как это имеет утечку памяти?
Эль Ронноко
4
@ElRonnoco: Ничто BSTRявно не освобождает , и это не объект .NET, поэтому сборщик мусора об этом тоже не позаботится. Сравните со stackoverflow.com/a/818709/103167, который был размещен 5 лет назад и не течет.
Бен Фойгт
Этот ответ не работает на платформах не Windows. PtrToStringAuto неверен для объяснения, см .: github.com/PowerShell/PowerShell/issues/…
К. Франк
-5

Если вы используете StringBuilderвместо a string, вы можете перезаписать фактическое значение в памяти, когда вы закончите. Таким образом, пароль не будет храниться в памяти, пока сборщик мусора не заберет его.

StringBuilder.Append(plainTextPassword);
StringBuilder.Clear();
// overwrite with reasonably random characters
StringBuilder.Append(New Guid().ToString());
Майкл Либен
источник
2
Хотя это действительно так, сборщик мусора все еще может перемещать буфер StringBuilder в памяти во время сжатия поколений, что приводит к ошибке «перезаписать фактическое значение», поскольку существует другая (или более) оставшаяся копия, которая не уничтожена.
Бен Фойгт
4
Это даже не дает удаленного ответа на вопрос.
Джей Салливан