C# SecureString Question

后端 未结 6 1512
忘掉有多难
忘掉有多难 2020-11-29 00:58

Is there any way to get the value of a SecureString without comprising security? For example, in the code below as soon as you do PtrToStringBSTR the string is no longer sec

6条回答
  •  误落风尘
    2020-11-29 02:01

    Here's a class I've written especially for this purpose. Is it completely, 100% hackproof? No - there's very little you can do to make an application 100% safe, but this class goes about as far as you can to protect yourself if you need to convert a SecureString into a String.

    Here's how you use the class:

    using(SecureStringToStringMarshaler sm = new SecureStringToStringMarshaler(secureString))
    {
        // Use sm.String here.  While in the 'using' block, the string is accessible
        // but pinned in memory.  When the 'using' block terminates, the string is zeroed
        // out for security, and garbage collected as usual.
    }
    

    Here's the class

    /// Copyright (C) 2010 Douglas Day
    /// All rights reserved.
    /// MIT-licensed: http://www.opensource.org/licenses/mit-license.php
    
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Security;
    using System.Runtime.InteropServices;
    using System.Runtime.CompilerServices;
    
    namespace DDay.Base
    {
        public class SecureStringToStringMarshaler : IDisposable
        {
            #region Private Fields
    
            private string _String;
            private SecureString _SecureString;
            private GCHandle _GCH;
    
            #endregion
    
            #region Public Properties
    
            public SecureString SecureString
            {
                get { return _SecureString; }
                set
                {
                    _SecureString = value;
                    UpdateStringValue();
                }
            }
    
            public string String
            {
                get { return _String; }
                protected set { _String = value; }
            } 
    
            #endregion
    
            #region Constructors
    
            public SecureStringToStringMarshaler()
            {
            }
    
            public SecureStringToStringMarshaler(SecureString ss)        
            {
                SecureString = ss;
            }
    
            #endregion
    
            #region Private Methods
    
            void UpdateStringValue()
            {
                Deallocate();
    
                unsafe
                {
                    if (SecureString != null)
                    {
                        int length = SecureString.Length;
                        String = new string('\0', length);
    
                        _GCH = new GCHandle();
    
                        // Create a CER (Contrained Execution Region)
                        RuntimeHelpers.PrepareConstrainedRegions();
                        try { }
                        finally
                        {
                            // Pin our string, disallowing the garbage collector from
                            // moving it around.
                            _GCH = GCHandle.Alloc(String, GCHandleType.Pinned);
                        }
    
                        IntPtr stringPtr = IntPtr.Zero;
                        RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(
                            delegate
                            {
                                // Create a CER (Contrained Execution Region)
                                RuntimeHelpers.PrepareConstrainedRegions();
                                try { }
                                finally
                                {
                                    stringPtr = Marshal.SecureStringToBSTR(SecureString);
                                }
    
                                // Copy the SecureString content to our pinned string
                                char* pString = (char*)stringPtr;
                                char* pInsecureString = (char*)_GCH.AddrOfPinnedObject();
                                for (int index = 0; index < length; index++)
                                {
                                    pInsecureString[index] = pString[index];
                                }
                            },
                            delegate
                            {
                                if (stringPtr != IntPtr.Zero)
                                {
                                    // Free the SecureString BSTR that was generated
                                    Marshal.ZeroFreeBSTR(stringPtr);
                                }
                            },
                            null);
                    }
                }
            }
    
            void Deallocate()
            {            
                if (_GCH.IsAllocated)
                {
                    unsafe
                    {
                        // Determine the length of the string
                        int length = String.Length;
    
                        // Zero each character of the string.
                        char* pInsecureString = (char*)_GCH.AddrOfPinnedObject();
                        for (int index = 0; index < length; index++)
                        {
                            pInsecureString[index] = '\0';
                        }
    
                        // Free the handle so the garbage collector
                        // can dispose of it properly.
                        _GCH.Free();
                    }
                }
            } 
    
            #endregion
    
            #region IDisposable Members
    
            public void Dispose()
            {
                Deallocate();
            }
    
            #endregion
        }
    }
    

    This code requires that you can compile unsafe code, but it works like a charm.

    Regards,

    -Doug

提交回复
热议问题