Вопрос С# SecureString
Есть ли способ получить значение SecureString без обеспечения безопасности? Например, в приведенном ниже коде, как только вы выполняете PtrToStringBSTR, строка больше не защищена, потому что строки неизменяемы, а сбор мусора является недетерминированным для строк.
IntPtr ptr = Marshal.SecureStringToBSTR(SecureString object);
string value = Marshal.PtrToStringBSTR(ptr);
Что делать, если есть способ получить char [] или byte [] неуправляемой строки BSTR? Будет ли это означать, что сбор мусора более предсказуем (поскольку вы использовали бы char [] или byte [], а не строку? Правильно ли это предположение, и если да, то как вы вернете char [] или байт []?
Ответы
Ответ 1
Это должно помочь вам: Marshaling SecureString Пароли для строки
Из статьи ключевые моменты:
- Вставьте строку в память.
- Используйте управляемые указатели для изменения System.String.
- Используйте сильные гарантии метода ExecuteCodeWithGuaranteedCleanup.
Ответ 2
Здесь класс, который я написал специально для этой цели. Это полностью, 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
Ответ 3
SecureStrings безопасны только в том случае, если вы их не используете. ) -;
1 вещь, которую вы не должны делать, это скопировать в строку (независимо от метода). Строка неизменна и может потенциально оставаться в памяти в течение длительного времени.
Скопировать его в char [] немного безопаснее, если вы предпримете меры предосторожности для обнуления этого массива как можно скорее. Но массив присутствует в памяти в течение некоторого времени, и это риск безопасности (нарушение).
К сожалению, поддержка SecureStrings в библиотеке очень мало. Наиболее распространенным способом работы с ними является один char за раз.
Изменить:
массив char[]
должен быть закреплен, а Mark Byers - ссылку на статью, делающую то же самое с закрепленной строкой. Это вопрос выбора, но риск строки состоит в том, что ее очень легко скопировать (передать ее некоторому методу, который выполняет Trim()
, будет достаточно).
Ответ 4
Предоставленная ссылка указывает на лучшее, что вы можете сделать, и это подход, который моя команда предприняла для решения этой проблемы (хотя мы не пошли на сложность использования ССВ). Я немного сомневался в использовании пиннинга, чтобы существенно разбить С# String неизменяемость, но он работает.
Ответ 5
Используйте Marshal.ZeroFreeBSTR:
EDIT: Да, создание новой строки создаст копию, поэтому вы потеряете контроль над очисткой содержимого. Вы можете получить доступ к char [], поместив указатель, возвращенный IntPtr.ToPointer() в небезопасном контексте:
IntPtr ptr = Marshal.SecureStringToBSTR(str);
unsafe
{
char *cp = (char*)ptr.ToPointer();
//access char[] through cp
}
Marshal.ZeroFreeBSTR(ptr);