Как преобразовать 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

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


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

Хотя ваш код не пропускает копию строки, он по-прежнему представляет собой яму отчаяния . Практически каждая операция над 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
Это даже не дает удаленного ответа на вопрос.
— Джей Салливан
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.