C#SecureString问题

时间:2009-11-25 23:37:20

标签: c# .net securestring

有没有办法在不包含安全性的情况下获取SecureString的值?例如,在下面的代码中,只要执行PtrToStringBSTR,字符串就不再安全,因为字符串是不可变的,垃圾收集对于字符串是不确定的。

IntPtr ptr = Marshal.SecureStringToBSTR(SecureString object);
string value = Marshal.PtrToStringBSTR(ptr);

如果有办法获取非托管BSTR字符串的char []或byte []怎么办?这是否意味着垃圾收集更可预测(因为你将使用char []或byte []而不是字符串?这个假设是正确的,如果是这样,你将如何得到char []或byte []?

6 个答案:

答案 0 :(得分:27)

这是我为此专门写的课程。是完全的,100%防黑客?不 - 你可以做很少的事情来使应用程序100%安全,但如果你需要将SecureString转换为String,这门课就可以保护你自己。

以下是您使用该课程的方式:

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

这是班级

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

此代码要求您可以编译unsafe代码,但它的作用就像魅力一样。

此致

-Doug

答案 1 :(得分:12)

这可以帮助您:Marshaling SecureString Passwords to String

从文章中,关键点是:

  • 将字符串固定在内存中。
  • 使用托管指针来改变System.String。
  • 使用ExecuteCodeWithGuaranteedCleanup方法的强保证。

答案 2 :(得分:9)

只要您不使用SecureStrings,SecureStrings就是安全的。 ) - ;

你不应该做的一件事是复制到一个字符串(无论方法如何)。该字符串是不可变的,可能会长时间保留在内存中。

只要您尽快采取将阵列归零的预防措施,将其复制到char []会更安全一些。但是阵列存在于内存中一段时间​​,这是一种安全风险(违规)。

不幸的是,图书馆中对SecureStrings的支持很少。最常用的方法是一次一个字符。

编辑:

应该固定char[]数组,并且Mark Byers提供指向使用固定字符串执行相同操作的文章的链接。这是一个选择问题,但字符串的风险是它很容易被复制(将它传递给某个执行Trim()的方法就足够了)。

答案 3 :(得分:1)

Mark提供的链接是您可以做的最好的,也是我的团队为解决这个问题所采取的方法(尽管我们没有考虑使用CER的复杂性)。我有点怀疑使用pinning来破坏C#String不变性,但它确实有效。

答案 4 :(得分:0)

使用Marshal.ZeroFreeBSTR

编辑:是的,创建一个新的String将创建一个副本,因此您将无法控制内容的清理。您可以通过在不安全的上下文中强制转换IntPtr.ToPointer()返回的指针来访问char []:

IntPtr ptr = Marshal.SecureStringToBSTR(str);
unsafe
{
    char *cp = (char*)ptr.ToPointer();
    //access char[] through cp
}

Marshal.ZeroFreeBSTR(ptr);

答案 5 :(得分:0)

这是一个还释放本机缓冲区的函数,因此您的内存中没有字符串。

    protected static string ConvertToUnsecureString(SecureString securePassword)
    {
        if (securePassword == null)
            throw new ArgumentNullException("securePassword");

        IntPtr unmanagedString = IntPtr.Zero;
        try
        {
            unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(securePassword);
            return Marshal.PtrToStringUni(unmanagedString);
        }
        finally
        {
            // Free the native buffer
            Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
        }
    }

Source