C#SecureString问题
有没有办法在不包含安全性的情况下获得SecureString的价值? 例如,在下面的代码中,只要执行PtrToStringBSTR,字符串就不再安全,因为字符串是不可变的,垃圾收集对于字符串是不确定的。
IntPtr ptr = Marshal.SecureStringToBSTR(SecureString object); string value = Marshal.PtrToStringBSTR(ptr);
如果有办法获取非托管BSTR字符串的char []或byte []怎么办? 这是否意味着垃圾收集更可预测(因为你将使用char []或byte []而不是字符串?这个假设是正确的,如果是这样,你将如何得到char []或byte []?
这应该可以帮助您:将SecureString密码密码连接到字符串
从文章中,关键点是:
- 将字符串固定在内存中。
- 使用托管指针来改变System.String。
- 使用ExecuteCodeWithGuaranteedCleanup方法的强保证。
这是我为此专门写的课程。 它是完全的,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. }
这是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 } }
此代码要求您可以编译unsafe
代码,但它的作用就像一个魅力。
问候,
-Doug
只要您不使用SecureStrings,SecureStrings就是安全的。 ) – ;
你不应该做的一件事是复制到一个字符串(无论方法如何)。 该字符串是不可变的,可能会长时间保留在内存中。
只要您尽快采取将该arrays归零的预防措施,将其复制到char []会更安全一些。 但是arrays存在于内存中一段时间,这是一种安全风险(违规)。
不幸的是,库中的SecureStrings支持很少。 使用它们的最常见方式是一次一个字符。
编辑:
应该固定char[]
数组,并且Mark Byers提供指向使用固定字符串执行相同操作的文章的链接。 这是一个选择问题,但字符串的风险是它很容易被复制(将它传递给执行Trim()
某个方法就足够了)。
Mark提供的链接是您可以做的最好的,也是我的团队为解决这个问题所采取的方法(尽管我们没有考虑使用CER的复杂性)。 我有点怀疑使用固定基本上打破C#字符串不变性,但它确实有效。
使用Marshal.ZeroFreeBSTR :
编辑:是的,创建一个新的String将创建一个副本,因此您将失去对内容清理的控制。 您可以通过在不安全的上下文中强制转换IntPtr.ToPointer()返回的指针来访问char []:
IntPtr ptr = Marshal.SecureStringToBSTR(str); unsafe { char *cp = (char*)ptr.ToPointer(); //access char[] through cp } Marshal.ZeroFreeBSTR(ptr);