C#安全字符串问题

C#安全字符串问题,c#,.net,securestring,C#,.net,Securestring,是否有任何方法可以在不包含安全性的情况下获取SecureString的值?例如,在下面的代码中,只要执行PtrToStringBSTR,字符串就不再安全,因为字符串是不可变的,并且字符串的垃圾收集是不确定的 IntPtr ptr = Marshal.SecureStringToBSTR(SecureString object); string value = Marshal.PtrToStringBSTR(ptr); 如果有办法获取非托管BSTR字符串的char[]或byte[]呢?这是否意味

是否有任何方法可以在不包含安全性的情况下获取SecureString的值?例如,在下面的代码中,只要执行PtrToStringBSTR,字符串就不再安全,因为字符串是不可变的,并且字符串的垃圾收集是不确定的

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

如果有办法获取非托管BSTR字符串的char[]或byte[]呢?这是否意味着垃圾收集更具可预测性(因为您将使用char[]或byte[]而不是字符串?此假设正确吗?如果正确,您将如何取回char[]或byte[]?

SecureStrings只有在不使用它们的情况下才是安全的)

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

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

不幸的是,库中很少支持SecureStrings。使用它们最常见的方式是一次使用一个字符

编辑:

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

这将有助于您:

从文章中可以看出,关键点是:

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

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

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

Marshal.ZeroFreeBSTR(ptr);

提供的链接标记是您所能做的最好的标记,也是我的团队解决这个问题所采取的方法(尽管我们没有讨论使用CER的复杂性)。我对使用钉住从本质上打破C#字符串不变性有点怀疑,但它确实有效。

我专门为此编写了一个类。它是完全、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
    }
}
///版权所有(C)2010道格拉斯日
///版权所有。
///麻省理工学院许可:http://www.opensource.org/licenses/mit-license.php
使用制度;
使用System.Collections.Generic;
使用系统文本;
使用系统安全;
使用System.Runtime.InteropServices;
使用System.Runtime.CompilerServices;
命名空间DDay.Base
{
公共类SecureStringToStringMarshaler:IDisposable
{
#区域专用字段
私有字符串_字符串;
私有SecureString\u SecureString;
私人GCHandle_GCH;
#端区
#区域公共财产
公共SecureString SecureString
{
获取{return\u SecureString;}
设置
{
_SecureString=值;
UpdateStringValue();
}
}
公共字符串
{
获取{return\u String;}
受保护集{u String=value;}
} 
#端区
#区域构造函数
公共安全StringToStringMarshaler()
{
}
公共SecureStringToStringMarshaler(SecureString ss)
{
SecureString=ss;
}
#端区
#区域私有方法
void UpdateStringValue()
{
解除分配();
不安全的
{
如果(SecureString!=null)
{
int length=SecureString.length;
字符串=新字符串('\0',长度);
_GCH=新的GCHandle();
//创建CER(约束执行区域)
RuntimeHelpers.PrepareConstrainedRegions();
试试{}
最后
{
//锁定我们的字符串,禁止垃圾收集器
//移动它。
_GCH=GCHandle.Alloc(字符串,GCHandleType.pinted);
}
IntPtr stringPtr=IntPtr.Zero;
RuntimeHelpers.ExecuteCodeWithGuarantedCleanup(
代表
{
//创建CER(约束执行区域)
RuntimeHelpers.PrepareConstrainedRegions();
试试{}
最后
{
stringPtr=Marshal.SecureStringToBSTR(SecureString);
}
//将SecureString内容复制到我们的固定字符串
char*pString=(char*)stringPtr;
char*pInsecureString=(char*)\u GCH.AddrOfPinnedObject();
for(int index=0;index    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);
        }
    }