SecureString को System.String में कैसे बदलें?


156

एक सिस्टम बनाकर अपने SecureString को असुरक्षित करने के बारे में सभी आरक्षण। इसे छोड़कर , यह कैसे किया जा सकता है?

मैं एक साधारण System.Security.SecureString को System.String में कैसे बदल सकता हूँ?

मुझे यकीन है कि आप में से कई लोग जो SecureString से परिचित हैं, वे जवाब देने जा रहे हैं कि एक SecureString को साधारण .NET स्ट्रिंग में बदलना नहीं चाहिए क्योंकि यह सभी सुरक्षा सुरक्षा को हटा देता है। मुझे पता है । लेकिन अभी मेरा कार्यक्रम वैसे भी साधारण तार के साथ सब कुछ करता है, और मैं इसकी सुरक्षा बढ़ाने की कोशिश कर रहा हूं और हालांकि मैं एक एपीआई का उपयोग करने जा रहा हूं जो मुझे सिक्योरस्ट्रिंग लौटाता है मैं अपनी सुरक्षा बढ़ाने के लिए इसका उपयोग करने की कोशिश नहीं कर रहा हूं।

मैं Mars.S.SureureStringToBSTR से वाकिफ हूं, लेकिन मुझे नहीं पता कि उस 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.6sec लिया। यह मेरे लिए धीमा है। क्या किसी को एक ही समय या कुछ और तेज मिलता है?
मूलांक

@radbyx एक त्वरित और गंदे परीक्षण सेटअप में, मैं इसे 76 में 1000 बार कॉल कर सकता हूं। पहले आह्वान में 0.3 एमएस और बाद में चालान ~ 0.07ms लगते हैं। आपका सिक्योर स्ट्रिंग कितना बड़ा है और फ्रेमवर्क के किस संस्करण का आप उपयोग कर रहे हैं?
रासमस फेबर

लंबाई ओम मेरे सिक्योरस्ट्रीमिंग 168 है। मैं .NET फ्रेमवर्क 3.5 का उपयोग कर रहा हूं यदि यह आपके प्रश्न का उत्तर देता है? मैं 5-10 गुना tryed है हमेशा सेकंड के आसपास 4.5-4.65 ~ मैं अपना समय पाने के लिए प्यार होता है
radbyx

@RasmusFaber मेरा बुरा, मैंने Database.GetConnectionString()अपने सिक्योरस्ट्रिंग को प्राप्त करने के लिए, आपके कोड में जोड़ दिया था, जो कि लगभग 5sec (और हाँ मुझे उस पर ध्यान देना चाहिए)! सब अच्छा। मुझे सही दिशा बताने के लिए धन्यवाद।
मूलांक

108

जाहिर है आप जानते हैं कि यह एक सिक्योरस्ट्रिंग के पूरे उद्देश्य को कैसे हराता है, लेकिन मैं इसे वैसे भी आराम करूंगा।

यदि आप एक-लाइनर चाहते हैं, तो यह प्रयास करें: (.NET 4 और केवल ऊपर)

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

जहां SecurePassword एक SecureString है।


10
यद्यपि यह उत्पादन में उद्देश्य को पराजित करता है, आपका समाधान इकाई परीक्षणों के लिए एकदम सही है। धन्यवाद।
beterthanlife

इससे मुझे यह पता लगाने में मदद मिली कि एक सिक्योरस्ट्रिंग (System.Security.SecureString) मेरे ApiController (वेबापी) को पारित नहीं किया जा रहा था। Thx
ग्रेनडाकोडर

5
PowerShell में नोट यह है[System.Net.NetworkCredential]::new('', $securePassword).Password
स्टेपn

1
@ Thecorrigible1 क्या आप विस्तृत कर सकते हैं? जैसे कब एक ''ही प्रकार नहीं है [String]::Empty? New-Object Net.Credentialमेरे लिए भी काम नहीं करता है: टाइप नहीं पा सकते हैं [Net.Credential]: सत्यापित करें कि इस प्रकार की असेंबली भरी हुई है
stijn

2
यह एक SecureString के उद्देश्य को हराता है क्योंकि यह आपके SecureString सामग्री की एक गैर-एन्क्रिप्टेड प्रतिलिपि को सामान्य स्ट्रिंग में बनाता है। हर बार जब आप ऐसा करते हैं, तो आप कम से कम एक (और कचरा संग्रह के साथ संभवतः कई) जोड़ रहे हैं, जो आपके अनएन्क्रिप्टेड स्ट्रिंग को मेमोरी में कॉपी करता है। यह कुछ सुरक्षा संवेदनशील अनुप्रयोगों के लिए एक जोखिम माना जाता है और सिक्योरस्ट्रिंग को विशेष रूप से जोखिम को कम करने के लिए लागू किया गया था।
स्टीव इन सीओ

49

डैंग। इसे पोस्ट करने के ठीक बाद मुझे इस लेख में उत्तर गहरा मिला । लेकिन अगर किसी को पता है कि इंटप्रेनर अनमैन्डेड, अनएन्क्रिप्टेड बफ़र को कैसे एक्सेस करना है, जो कि यह तरीका एक्सपोज़ करता है, तो एक बार में एक बाइट ताकि मुझे अपनी सिक्योरिटी हाई रखने के लिए इसमें से एक प्रबंधित स्ट्रिंग ऑब्जेक्ट न बनाना पड़े, कृपया एक उत्तर जोड़ें। :)

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

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

आप निश्चित रूप से unsafeकीवर्ड और ए का उपयोग कर सकते हैं char*, बस कॉल bstr.ToPointer()और कास्ट करें।
बेन वोइगट

@BenVoigt BSTR के पास सुरक्षा के लिए स्ट्रिंग डेटा के बाद एक शून्य टर्मिनेटर है, लेकिन स्ट्रिंग में एम्बेडेड वर्णों की अनुमति देता है। तो यह उससे थोड़ा अधिक जटिल है, आपको उस सूचक से पहले बैठने वाली लंबाई उपसर्ग को भी पुनः प्राप्त करना होगा। docs.microsoft.com/en-us/prepret-versions/windows/desktop/…
Wim Coenen

@WimCoenen: सत्य लेकिन महत्वहीन। बीटीएस में संग्रहीत लंबाई पहले से उपलब्ध लंबाई की एक प्रति होगी SecureString.Length
बेन वोइगट

@BenVoigt आह, मेरी बुर। मुझे लगा कि सिक्योरस्ट्रिंग ने स्ट्रिंग के बारे में कोई जानकारी उजागर नहीं की है।
विम कोएनेन

@WimCoenen: SecureString, मूल्य छिपाने की कोशिश कर नहीं किया गया है यह इस तरह कचरा एकत्र स्मृति, पृष्ठ फ़ाइल, आदि इरादा जैसे क्षेत्रों जो मज़बूती से ओवरराइट नहीं किया जा सकता है, का रूप दिया गया से मूल्य की प्रतियां को रोकने के लिए कोशिश कर रहा है यह है कि जब SecureStringजीवन समाप्त होता है, पूरी तरह से स्मृति में रहस्य की कोई प्रति नहीं बची है। यह आपको प्रतिलिपि बनाने और लीक करने से नहीं रोकता है, लेकिन यह कभी नहीं करता है।
बेन Voigt

15

मेरी राय में, इसे हल करने के लिए विस्तार विधियां सबसे आरामदायक तरीका हैं।

मैंने स्टीव को सीओ के उत्कृष्ट उत्तर में ले लिया और इसे एक विस्तार वर्ग में रखा, इस प्रकार, एक दूसरी विधि के साथ मैंने दूसरी दिशा (स्ट्रिंग -> सुरक्षित स्ट्रिंग) का समर्थन करने के लिए जोड़ा, ताकि आप एक सुरक्षित स्ट्रिंग बना सकें और इसे रूपांतरित कर सकें। बाद में एक सामान्य स्ट्रिंग:

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को संलग्न करना सबसे अच्छा होगा ।

इस स्निपेट में सिक्योरस्ट्रीम को डिक्रिप्ट करने के लिए कार्यान्वयन होगा:

  1. मेमोरी में स्ट्रिंग को पिन करें (जो कि आप करना चाहते हैं, लेकिन यहां अधिकांश उत्तरों से गायब प्रतीत होता है)।
  2. दर्रा इसके संदर्भ समारोह / कार्रवाई प्रतिनिधि को।
  3. इसे मेमोरी से स्क्रब करें और जीसी को 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
समग्र दृष्टिकोण का वादा किया गया है, लेकिन मैं कामयाब स्ट्रिंग है जो असुरक्षित होता है pinning पर अपने प्रयास नहीं लगता कि (सादे-पाठ) प्रतिलिपि प्रभावी है: क्या आप के बजाय pinning रहे हैं मूल स्ट्रिंग उद्देश्य यह है कि आप के लिए शुरू कर दिया है 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कंस्ट्रक्टर के पास भेज देता है, जो इसे स्वीकार नहीं करता है 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 समाधान और जॉन Flaherty सुधार के अनुसार अंतिम कार्य समाधान है:

    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 साल पहले पोस्ट किया गया था और लीक नहीं करता है।
बेन वोइगट

यह उत्तर गैर विंडोज़ प्लेटफार्मों पर काम नहीं करता है। PtrToStringAuto एक स्पष्टीकरण के लिए गलत है देखें: github.com/PowerShell/PowerShell/issues/…
। फ्रैंक

-5

यदि आप StringBuilderइसके बजाय का उपयोग करते हैं string, तो आप स्मृति में वास्तविक मूल्य को ओवरराइट कर सकते हैं जब आप कर रहे हैं। इस तरह से पासवर्ड मेमोरी में तब तक नहीं लटका रहेगा जब तक कि कचरा संग्रह उसे उठा न ले।

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

2
हालांकि यह सच है, कचरा संग्राहक अभी भी जनरेशनल कंपटीशन के दौरान मेमोरी में StringBuilder बफर को स्थानांतरित कर सकता है, जो "वास्तविक मान को अधिलेखित करता है" विफल हो जाता है, क्योंकि एक और (या अधिक) बचे हुए प्रतिलिपि है जो नष्ट नहीं हुई है।
बेन वोइग्ट

4
यह भी दूर से सवाल का जवाब नहीं है।
जे। सुलिवन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.