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