C # Pregunta de SecureString

¿Hay alguna manera de obtener el valor de un SecureString sin incluir seguridad? Por ejemplo, en el siguiente código tan pronto como haga PtrToStringBSTR, la cadena ya no es segura porque las cadenas son inmutables y la recolección de basura no es determinista para las cadenas.

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

¿Qué pasa si hay una forma de obtener un char [] o un byte [] de la cadena BSTR no administrada? ¿Eso significaría que la recolección de basura es más predecible (ya que estarías usando un char [] o un byte [] en lugar de una cadena? ¿Es correcta esta suposición? De ser así, ¿cómo recuperarías el carácter [] o el byte []?

Esto debería ayudarle: Marshaling contraseñas SecureString a String

Del artículo, los puntos clave son:

  • Pin la cadena en la memoria.
  • Utilice punteros administrados para mutar el System.String.
  • Utilice las sólidas garantías del método ExecuteCodeWithGuaranteedCleanup.

Aquí hay una clase que escribí especialmente para este propósito. ¿Es completamente 100% pirata informático? No, hay muy poco que puede hacer para que una aplicación sea 100% segura, pero esta clase va lo más lejos posible para protegerse si necesita convertir un SecureString en un String .

Así es como usa la clase:

 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. } 

Aquí está la clase

 /// 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 } } 

Este código requiere que puedas comstackr unsafe código unsafe , pero funciona como un amuleto.

Saludos,

-Doug

SecureStrings solo son seguros siempre que no los uses. ) -;

Lo 1 que no debes hacer es copiar a una cadena (independientemente del método). La cadena es inmutable y puede permanecer en la memoria durante mucho tiempo.

Copiarlo en un char [] es un poco más seguro siempre que tome la precaución de poner a cero esa matriz lo antes posible. Pero la matriz está presente en la memoria por un tiempo y eso es un riesgo de seguridad (violación).

Desafortunadamente, hay muy poco soporte para SecureStrings en la biblioteca. La forma más común de trabajar con ellos es un char a la vez.

Editar:

la matriz char[] debe anclar, y Mark Byers proporciona un enlace a un artículo que hace lo mismo con una cadena anclada. Es una cuestión de elección, pero el riesgo de la cadena es que es muy fácil copiarla (pasarla a algún método que realice un Trim() sería suficiente).

El enlace que Mark proporcionó es sobre lo mejor que puede hacer, y es el enfoque que mi equipo ha tomado para abordar este problema (aunque no fuimos a la complejidad del uso de RCE). Tenía un poco de dudas sobre el uso de fijación para esencialmente romper la inmutabilidad de la cadena C #, pero funciona.

Use Marshal.ZeroFreeBSTR :

EDITAR: Sí, la creación de una nueva Cadena creará una copia, por lo que perderá el control sobre la limpieza de los contenidos. Puede acceder al char [] colocando el puntero devuelto por IntPtr.ToPointer () en un contexto inseguro:

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