有没有办法在不包含安全性的情况下获取SecureString的值?例如,在下面的代码中,只要执行PtrToStringBSTR,字符串就不再安全,因为字符串是不可变的,垃圾收集对于字符串是不确定的。
IntPtr ptr = Marshal.SecureStringToBSTR(SecureString object);
string value = Marshal.PtrToStringBSTR(ptr);
如果有办法获取非托管BSTR字符串的char []或byte []怎么办?这是否意味着垃圾收集更可预测(因为你将使用char []或byte []而不是字符串?这个假设是正确的,如果是这样,你将如何得到char []或byte []?
答案 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
从文章中,关键点是:
答案 2 :(得分:9)
只要您不使用SecureStrings,SecureStrings就是安全的。 ) - ;
你不应该做的一件事是复制到一个字符串(无论方法如何)。该字符串是不可变的,可能会长时间保留在内存中。
只要您尽快采取将阵列归零的预防措施,将其复制到char []会更安全一些。但是阵列存在于内存中一段时间,这是一种安全风险(违规)。
不幸的是,图书馆中对SecureStrings的支持很少。最常用的方法是一次一个字符。
编辑:
应该固定char[]
数组,并且Mark Byers提供指向使用固定字符串执行相同操作的文章的链接。这是一个选择问题,但字符串的风险是它很容易被复制(将它传递给某个执行Trim()
的方法就足够了)。
答案 3 :(得分:1)
Mark提供的链接是您可以做的最好的,也是我的团队为解决这个问题所采取的方法(尽管我们没有考虑使用CER的复杂性)。我有点怀疑使用pinning来破坏C#String不变性,但它确实有效。
答案 4 :(得分:0)
编辑:是的,创建一个新的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);
}
}