Nasıl SecureString System.String dönüştürmek için?


156

Bir System.String oluşturarak SecureString'inizin güvenliğinin kaldırılmasına ilişkin tüm çekinceler bir kenara nasıl yapılabilir?

Sıradan bir System.Security.SecureString'i System.String'e nasıl dönüştürebilirim?

SecureString'e aşina olan birçoğunuz, tüm güvenlik korumalarını kaldırdığı için bir SecureString'i normal bir .NET dizesine dönüştürmemesi gerektiğini yanıtlayacağınızdan eminim. Biliyorum . Ama şu anda benim program zaten sıradan dizeleri ile her şeyi yapar, ve ben onun güvenliğini artırmak için çalışıyorum ve bana bir SecureString döndüren bir API kullanacak olmasına rağmen ben güvenliğimi artırmak için kullanmaya çalışmıyorum .

Mareşal.SecureStringToBSTR'ın farkındayım, ama bu BSTR'yi nasıl alıp bir System.String yapmayı bilmiyorum.

Bunu neden yapmak istediğimi bilmek isteyenler için, bir kullanıcıdan bir şifre alıyorum ve kullanıcıyı bir web sitesinde oturum açmak için bir html formu POST olarak gönderiyorum. Yani ... bu gerçekten yönetilen, şifrelenmemiş arabelleklerle yapılmalıdır. Yönetilmeyen, şifrelenmemiş ara belleğe bile erişebilseydim, ağ akışında bayt bayt akış yazma yapabileceğimi ve parolanın tüm yol boyunca güvenli olmasını umduğumu hayal ediyorum. Bu senaryolardan en az birine cevap vermeyi umuyorum.

Yanıtlar:


192

System.Runtime.InteropServices.MarshalSınıfı kullanın :

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

Yönetilen dize nesnesi oluşturmaktan kaçınmak istiyorsanız, ham verilere aşağıdakileri kullanarak erişebilirsiniz 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
Yıllar sonra bile oylarımı aldım, yardımın için teşekkürler! Sadece hızlı bir not: Bu, kendi belleğinde statik olarak da çalışır.
John Suit

Kullandım StopWatchve SecureStringToString4.6 saniye sürdüm. Benim için yavaşlamak. Herkes aynı zamanda falan daha hızlı olur mu?
radbyx

@radbyx Hızlı ve kirli bir test kurulumunda 76 ms'de 1000 kez diyebilirim. İlk çağırma 0.3 ms sürer ve sonraki çağırma ~ 0.07ms. Güvenli dizeniz ne kadar büyük ve çerçevenin hangi sürümünü kullanıyorsunuz?
Rasmus Faber

SecureString uzunluğum 168. Sorunuzu cevapladıysa .NET Framework 3.5 kullanıyorum? Ben 5-10 kez denedim her zaman yaklaşık 4.5-4.65 sn ~ Ben zaman almak isterdim
radbyx

@RasmusFaber Benim kötüyüm, Database.GetConnectionString()neredeyse 5 saniye süren kötü kısmı olan secureString'i almak için kodunuza bir ekledim (ve evet buna bakmalıyım! :) Kodunuz kronomda .00 mili saniye sürdü, bu yüzden hepsi iyi. Beni doğru yöne yönlendirdiğiniz için teşekkürler.
radbyx

108

Açıkçası, bunun bir SecureString'in tüm amacını nasıl yendiğini biliyorsunuz, ama yine de yeniden ifade edeceğim.

Tek astar istiyorsanız, şunu deneyin: (yalnızca .NET 4 ve üstü)

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

SecurePassword bir SecureString olduğunda.


10
Üretimdeki amacı yenmekle birlikte, çözümünüz birim testleri için mükemmeldir. Teşekkürler.
beterthanlife

Bu, bir SecureString'in (System.Security.SecureString) ApiController (webapi) 'ye geçirilmediğini anlamama yardımcı oldu. Thx
granadaCoder

5
PowerShell'de not bu[System.Net.NetworkCredential]::new('', $securePassword).Password
stijn

1
@ TheIncorrigible1 biraz ayrıntı verebilir misiniz? Örneğin, ne zaman ''aynı tip değildir [String]::Empty? Ayrıca New-Object Net.Credentialbenim için çalışmıyor: [Net.Credential] türü bulunamıyor: bu türü içeren montajın yüklendiğini doğrulayın
stijn

2
SecureString'in amacını bozar çünkü SecureString içeriğinizin şifrelenmemiş bir kopyasını normal bir dizeye yapar. Bunu her yaptığınızda, şifrelenmemiş dizenizin en az bir (ve muhtemelen Çöp Toplama ile) kopyasını belleğe eklersiniz. Bu, bazı güvenlik duyarlı uygulamalar için bir risk olarak kabul edilir ve riski azaltmak için özel olarak SecureString uygulanmıştır.
Steve In CO

49

Dang. bunu gönderdikten hemen sonra bu makalenin derinliklerinde buldum . Ancak kimse IntPtr yönetilmeyen, şifrelenmemiş arabellek bu yöntemin ortaya koyacağını biliyorsa, benim güvenliğini yüksek tutmak için bir yönetilen dize nesnesi oluşturmak zorunda kalmamak için bir kerede bir bayt, lütfen bir yanıt ekleyin. :)

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

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

Kesinlikle kullanabilirsiniz unsafeanahtar kelime ve bir char*, sadece çağrı bstr.ToPointer()ve döküm.
Ben Voigt

@BenVoigt BSTR, güvenlik için dize verilerinden sonra boş bir sonlandırıcıya sahiptir, ancak dizeye katıştırılmış boş karakterlere de izin verir. Bu yüzden bundan biraz daha karmaşık, aynı zamanda o işaretçiden önce gelen uzunluk önekini de almanız gerekiyor. docs.microsoft.com/tr-tr/previous-versions/windows/desktop/…
Wim Coenen

@WimCoenen: Doğru ama önemsiz. BSTR'de saklanan uzunluk, halihazırda mevcut olan uzunluğun bir kopyası olacaktır SecureString.Length.
Ben Voigt

@ BenVoigt ah, benim hatam. SecureString dize hakkında herhangi bir bilgi ortaya koymadı düşündüm.
Wim Coenen

@WimCoenen: SecureStringdeğeri saklamaya çalışmıyor, değerin kopyalarının çöp toplanan bellek, sayfa dosyası vb. Gibi güvenilir bir şekilde üzerine yazılamayacak bölgelere yapılmasını engellemeye çalışıyor. Amaç, SecureStringömür boyu sona erdiğinde, kesinlikle sırrın hiçbir kopyası hafızada kalmaz. Bir kopyasını almanızı ve sızdırmanızı engellemez, ancak asla yapmaz.
Ben Voigt

15

Kanımca, genişletme yöntemleri bunu çözmenin en rahat yoludur.

Çektiğim CO'nun Steve mükemmel cevap güvenli bir dize oluşturmak ve dönüştürmek, böylece, hem de - I (> güvenli dize dize) diğer yöne desteklemek için eklenen ve ikinci bir yöntem ile birlikte, aşağıdaki gibi bir uzatma sınıf koymak daha sonra normal bir karakter dizisi:

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

Bununla, şimdi dizelerinizi ileri geri basitçe dönüştürebilirsiniz :

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

Ancak, kod çözme yönteminin yalnızca test için kullanılması gerektiğini unutmayın.


14

SecureStringBağımlı fonksiyonlar (bir kez sabitlenmiş) bellekte şifresi çözülmüş dize üzerinde daha iyi kontrol için anonim bir fonksiyonda bağımlı mantık kapsüllemek için en iyi olacağını düşünüyorum .

Bu snippet'te SecureStrings'in şifresini çözmek için uygulama:

  1. Dizeyi belleğe sabitleyin (yapmak istediğiniz şeydir, ancak burada çoğu cevapta eksik gibi görünüyor).
  2. Geçiş onun referansını Func / Aksiyon temsilciye.
  3. Bellekten silin ve finallyblokta GC'yi serbest bırakın .

Bu, arayanların "standardize edilmesini" ve daha az arzu edilen alternatiflere güvenmeyi vs. karşılamayı çok daha kolay hale getirir:

  • Şifresi çözülmüş dizeyi bir string DecryptSecureString(...)yardımcı işlevden döndürme .
  • Gerektiğinde bu kodu çoğaltmak.

Burada dikkat edin, iki seçeneğiniz var:

  1. static T DecryptSecureString<T>bu da Functemsilcinin sonucuna arayandan erişmenizi sağlar ( DecryptSecureStringWithFunctest yönteminde gösterildiği gibi ).
  2. static void DecryptSecureStringAction( DecryptSecureStringWithActiontest yönteminde gösterildiği gibi) aslında hiçbir şey döndürmek istemediğiniz / ihtiyaç duymadığınız durumlarda bir temsilci kullanan "geçersiz" bir sürümdür .

Her ikisi için örnek kullanım StringsTestdahil sınıfta bulunabilir .

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

Açıkçası, bu, bu fonksiyonun aşağıdaki şekilde kötüye kullanılmasını engellemez, bu yüzden bunu yapmamaya dikkat edin:

[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
}

Mutlu kodlama!


Bölüm Marshal.Copy(new byte[insecureString.Length], 0, insecureStringPointer, (int)insecureString.Length);yerine neden kullanılmıyor fixed?
1919

@ sclarke81, iyi bir fikir, ancak kullanım gerekir [char], değil [byte].
mklement0

1
Genel yaklaşım umut vericidir, ancak güvensiz (düz metin) kopyayı içeren yönetilen dizeyi sabitleme girişiminizin etkili olduğunu düşünmüyorum: sabitlediğiniz şey, başlattığınız orijinal dize nesnesidir String.Empty, tarafından oluşturulan ve döndürülen yeni tahsis edilen örnek değil Marshal.PtrToStringUni().
mklement0

7

Ben rdev5 cevap dayalı aşağıdaki uzantı yöntemleri oluşturdu . Yönetilen dizeyi sabitlemek önemlidir, çünkü çöp toplayıcının onu hareket ettirmesini ve silemediğiniz kopyaları geride bırakmasını engeller.

Benim çözümümün avantajı, güvenli olmayan bir kod gerekmediğidir.

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

Kodunuz dizenin bir kopyasını sızdırmazken, yine de umutsuzluk çukurunu temsil eder . System.StringNesnedeki neredeyse her işlem sabitlenmemiş ve düzeltilmemiş kopyalar oluşturur. Bu yüzden bu yerleşik değil SecureString.
Ben Voigt

Güzel, tüm dize dışarı sıfıra olsa kullanmak gerekecek new char[length](veya çarpma lengthile sizeof(char)).
mklement0

@BenVoigt: actionTemsilci geçici, sabitlenmiş, sonra sıfırlanmış dizenin kopyalarını oluşturmadığı sürece , bu yaklaşım SecureStringkendisi kadar güvenli veya güvenli olmamalıdır - ikincisini kullanmak için düz metin gösterimi de güvenli dizelerin işletim sistemi düzeyinde yapılar olmadığı göz önüne alındığında; göreceli güvenlik, bu dizenin ömrünü kontrol etmek ve kullanımdan sonra silinmesini sağlamaktır.
mklement0

@ mklement0: SecureStringher yerde kopya yapan üye işlevleri ve aşırı yüklenmiş işleçleri yoktur. System.Stringyapar.
Ben Voigt

1
@ mklement0: Oldukça saçma olan, onu kabul eden bir yapıcıya geçirdiği düşünülürse NetworkCredentiala SecureString.
Ben Voigt

0

Bu C # kodu istediğiniz şeydir.

%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

Ben sclarke81 tarafından Bu cevap türetilmiştir . Cevabını seviyorum ve türevi kullanıyorum ama sclarke81'in bir hatası var. İtibarım yok, bu yüzden yorum yapamam. Sorun, başka bir cevabı garanti etmeyecek kadar küçük görünüyor ve onu düzenleyebilirim. Ben de yaptım. Reddedildi. Şimdi başka bir cevabımız var.

sclarke81 Umarım bunu görürsünüz (sonunda):

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

olmalı:

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

Ve hata düzeltmesi ile tam cevap:


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

İyi bir nokta; Ben OP bildirmek gerekir başvurulan cevap, bir yorum bıraktım.
mklement0

0

Sclarke81 çözümüne ve John Flaherty düzeltmelerine göre nihai çalışma çözümü:

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

Bu cevabın bir bellek sızıntısı var.
Ben Voigt

@BenVoigt Lütfen bunun nasıl bellek sızıntısı olduğunu açıklayabilir misiniz?
El Ronnoco

4
@ElRonnoco: Hiçbir şey BSTRaçıkça serbest bırakılmaz ve bu bir .NET nesnesi değildir, bu yüzden çöp toplayıcı da onunla ilgilenmez. 5 yıl önce yayınlanan ve sızıntı yapmayan stackoverflow.com/a/818709/103167 ile karşılaştırın .
Ben Voigt

Bu yanıt windows olmayan platformlarda çalışmaz. PtrToStringAuto bir açıklama için yanlıştır bkz: github.com/PowerShell/PowerShell/issues/…
K. Frank

-5

Yerine a StringBuilderkullanırsanız string, işiniz bittiğinde bellekteki gerçek değerin üzerine yazabilirsiniz. Bu şekilde, çöp toplama işlemi toplayana kadar parola bellekte takılmaz.

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

2
Bu doğru olsa da, çöp toplayıcı, StringBuilder arabelleğini hala nesnel sıkıştırma sırasında bellekte hareket ettirebilir;
Ben Voigt

4
Bu, soruyu uzaktan bile cevaplamıyor.
Jay Sullivan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.