¿Cuándo necesitaría un SecureString en .NET?

179

Estoy tratando de entender el propósito de SecureString de .NET. De MSDN:

Una instancia de la clase System.String es inmutable y, cuando ya no se necesita, no se puede programar mediante programación para la recolección de basura; es decir, la instancia es de solo lectura después de su creación y no es posible predecir cuándo se eliminará de la memoria de la computadora. En consecuencia, si un objeto String contiene información confidencial, como una contraseña, un número de tarjeta de crédito o datos personales, existe el riesgo de que la información se revele después de ser utilizada porque su aplicación no puede eliminar los datos de la memoria de la computadora.

Un objeto SecureString es similar a un objeto String en que tiene un valor de texto. Sin embargo, el valor de un objeto SecureString se cifra automáticamente, puede modificarse hasta que su aplicación lo marque como de solo lectura y su aplicación o el recolector de basura de .NET Framework pueden eliminarlo de la memoria de la computadora.

El valor de una instancia de SecureString se cifra automáticamente cuando se inicializa la instancia o cuando se modifica el valor. Su aplicación puede hacer que la instancia sea inmutable y evitar modificaciones adicionales invocando el método MakeReadOnly.

¿El cifrado automático es la gran recompensa?

¿Y por qué no puedo simplemente decir:

SecureString password = new SecureString("password");

en vez de

SecureString pass = new SecureString();
foreach (char c in "password".ToCharArray())
    pass.AppendChar(c);

¿Qué aspecto de SecureString me estoy perdiendo?

Richard Morgan
fuente
3
11 años después y MS ya no recomienda SecureStringnuevos desarrollos: github.com/dotnet/platform-compat/blob/master/docs/DE0001.md
Matt Thomas

Respuestas:

4

Dejaría de usar SecureString. Parece que los chicos de PG están dejando de apoyarlo. Posiblemente incluso lo tire en el futuro: https://github.com/dotnet/apireviews/tree/master/2015-07-14-securestring .

Deberíamos eliminar el cifrado de SecureString en todas las plataformas en .NET Core. Deberíamos dejar obsoleto SecureString. Probablemente no deberíamos exponer SecureString en .NET Core.

Joe Healy
fuente
1
El enlace está muerto pero parece continuar: docs.microsoft.com/en-us/dotnet/api/… .. con una guía muy débil sobre el camino hacia adelante execpt "no use credenciales" - github.com/dotnet/platform- compat / blob / master / docs / DE0001.md .. ¡ tampoco te atrevas a usar una contraseña para proteger la clave privada de tus certificados!
felickz
1
Después de 11 años, esta respuesta parece ser la "nueva" correcta. Los enlaces parecen estar obsoletos, pero la guía de MS es: SecureString no debe usarse
Richard Morgan
109

Algunas partes del marco que actualmente utilizamos SecureString:

El objetivo principal es reducir la superficie de ataque, en lugar de eliminarla. SecureStringsestán "anclados" en la RAM para que el recolector de basura no lo mueva ni haga copias de él. También se asegura de que el texto sin formato no se escriba en el archivo de intercambio o en los volcados del núcleo. Sin embargo, el cifrado se parece más a la ofuscación y no detendrá a un hacker determinado, que podría encontrar la clave simétrica utilizada para cifrarlo y descifrarlo.

Como otros han dicho, la razón por la que tienes que crear un SecureString carácter por carácter se debe a la primera falla obvia de hacer lo contrario: presumiblemente ya tiene el valor secreto como una cadena simple, entonces, ¿cuál es el punto?

SecureStrings son el primer paso para resolver un problema de huevo y gallina, por lo que, aunque la mayoría de los escenarios actuales requieren convertirlos de nuevo en cadenas regulares para usarlos, su existencia en el marco ahora significa un mejor apoyo para ellos en el futuro, al menos hasta el punto en que su programa no tiene que ser el eslabón débil.

Chris Wenham
fuente
2
Lo encontré mirando la propiedad Contraseña de ProcessStartInfo; sin siquiera prestar atención al tipo, simplemente lo configuré en una cadena normal hasta que el compilador me ladró.
Richard Morgan
No será fácil encontrar la clave de cifrado simétrica, ya que SecureString se basa en DPAPI, que no almacena exactamente una clave en texto sin formato ...
AviD
1
Además, no es tanto el problema del huevo y la gallina, ya que no es un reemplazo para el cifrado en el almacenamiento, pero es una solución alternativa para cadenas .NET administradas e inmutables.
AviD
2
"presumiblemente ya tienes el valor secreto como una cadena simple, entonces, ¿cuál es el punto?" ¿Hay una respuesta a esta pregunta? Parece que, en el mejor de los casos, es una solución "mejor que nada" si tiene la intención de mantener una contraseña almacenada en la memoria durante un período prolongado.
xr280xr
2
¿Qué pasa con tener algunos ejemplos de uso de código simple? Creo que entendería mejor cómo y cuándo usarlo.
codea
37

Editar : no use SecureString

La orientación actual ahora dice que la clase no debe usarse. Los detalles se pueden encontrar en este enlace: https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md

Del artículo:

DE0001: SecureString no debe usarse

Motivación

  • El propósito de SecureStringes evitar tener secretos almacenados en la memoria del proceso como texto sin formato.
  • Sin embargo, incluso en Windows, SecureStringno existe como un concepto de sistema operativo.
    • Simplemente hace que la ventana acorte el texto plano; no lo impide completamente ya que .NET todavía tiene que convertir la cadena a una representación de texto sin formato.
    • El beneficio es que la representación de texto sin formato no se queda como una instancia de System.String: la vida útil del búfer nativo es más corta.
  • El contenido de la matriz no está cifrado, excepto en .NET Framework.
    • En .NET Framework, el contenido de la matriz char interna está encriptado. .NET no admite el cifrado en todos los entornos, ya sea por falta de API o por problemas de administración de claves.

Recomendación

No usar SecureString para el nuevo código. Al portar código a .NET Core, tenga en cuenta que el contenido de la matriz no está cifrado en la memoria.

El enfoque general de tratar con credenciales es evitarlas y confiar en otros medios para autenticarse, como certificados o autenticación de Windows.

Finalizar edición: Resumen original a continuación

Muchas respuestas geniales; Aquí hay una sinopsis rápida de lo que se ha discutido.

Microsoft ha implementado la clase SecureString en un esfuerzo por proporcionar una mejor seguridad con información confidencial (como tarjetas de crédito, contraseñas, etc.). Proporciona automáticamente:

  • cifrado (en caso de volcados de memoria o almacenamiento en caché de páginas)
  • anclado en la memoria
  • capacidad de marcar como solo lectura (para evitar más modificaciones)
  • construcción segura al NO permitir que se pase una cadena constante

Actualmente, SecureString tiene un uso limitado, pero se espera una mejor adopción en el futuro.

Según esta información, el constructor de SecureString no solo debe tomar una cadena y dividirla en una matriz de caracteres, ya que la cadena deletreada anula el propósito de SecureString.

Información adicional:

  • Una publicación del blog de .NET Security que habla de lo mismo que se trata aquí.
  • Y otro que lo vuelve a visitar y menciona una herramienta que PUEDE volcar el contenido de SecureString.

Editar: Me resultó difícil elegir la mejor respuesta, ya que hay buena información en muchos; Lástima que no hay opciones de respuesta asistida.

Richard Morgan
fuente
19

Respuesta corta

¿Por qué no puedo decir:

SecureString password = new SecureString("password");

Porque ahora lo tienes passworden memoria; sin forma de borrarlo, que es exactamente el punto de SecureString .

Respuesta larga

La razón por la que existe SecureString es porque no puede usar ZeroMemory para borrar datos confidenciales cuando haya terminado con ella. Existe para resolver un problema que existe debido al CLR.

En una aplicación nativa normal llamarías SecureZeroMemory:

Llena un bloque de memoria con ceros.

Nota : SecureZeroMemory es idéntico a ZeroMemory, excepto que el compilador no lo optimizará.

El problema es que no puedes llamar ZeroMemoryni SecureZeroMemorydentro de .NET. Y en .NET las cadenas son inmutables; ni siquiera puede sobrescribir el contenido de la cadena como puede hacerlo en otros idiomas:

//Wipe out the password
for (int i=0; i<password.Length; i++)
   password[i] = \0;

¿Entonces que puedes hacer? ¿Cómo proporcionamos la capacidad en .NET para borrar una contraseña o número de tarjeta de crédito de la memoria cuando hayamos terminado con ella?

La única forma en que se puede hacer es colocar la cadena en algún bloque de memoria nativa , donde luego puede llamar ZeroMemory. Un objeto de memoria nativa como:

  • un BSTR
  • un HGLOBAL
  • Memoria no administrada de CoTaskMem

SecureString devuelve la habilidad perdida

En .NET, las cadenas no se pueden borrar cuando haya terminado con ellas:

  • son inmutables; no puedes sobrescribir sus contenidos
  • no puedes Disposede ellos
  • su limpieza está a merced del recolector de basura

SecureString existe como una forma de pasar la seguridad de las cadenas y poder garantizar su limpieza cuando sea necesario.

Hiciste la pregunta:

¿Por qué no puedo decir:

SecureString password = new SecureString("password");

Porque ahora tienes password en memoria; sin forma de limpiarlo. Está atascado allí hasta que el CLR decida reutilizar esa memoria. Nos has devuelto a donde comenzamos; una aplicación en ejecución con una contraseña que no podemos eliminar, y donde un volcado de memoria (o Process Monitor) puede ver la contraseña.

SecureString utiliza la API de protección de datos para almacenar la cadena encriptada en la memoria; de esa manera, la cadena no existirá en archivos de intercambio, volcados por caída o incluso en la ventana de variables locales con un colega revisando su debería.

¿Como leo la contraseña?

Entonces es la pregunta: ¿cómo interactúo con la cadena? Absolutamente no quieres un método como:

String connectionString = secureConnectionString.ToString()

porque ahora estás de vuelta donde empezaste: una contraseña de la que no puedes deshacerte. Desea obligar a los desarrolladores a manejar la cadena sensible correctamente, para que pueda borrarse de la memoria.

Es por eso que .NET proporciona tres prácticas funciones de ayuda para ordenar un SecureString en una memoria no administrada:

Convierte la cadena en un blob de memoria no administrado, lo maneja y luego lo limpia nuevamente.

Algunas API aceptan SecureStrings . Por ejemplo, en ADO.net 4.5, SqlConnection.Credential toma un conjunto SqlCredential :

SqlCredential cred = new SqlCredential(userid, password); //password is SecureString
SqlConnection conn = new SqlConnection(connectionString);
conn.Credential = cred;
conn.Open();

También puede cambiar la contraseña dentro de una Cadena de conexión:

SqlConnection.ChangePassword(connectionString, cred, newPassword);

Y hay muchos lugares dentro de .NET donde continúan aceptando un String simple para fines de compatibilidad, luego rápidamente lo cambian y lo colocan en un SecureString.

¿Cómo poner texto en SecureString?

Esto todavía deja el problema:

¿Cómo obtengo una contraseña en SecureString en primer lugar?

Este es el desafío, pero el punto es hacerle pensar en seguridad.

A veces, la funcionalidad ya está disponible para usted. Por ejemplo, el control WPF PasswordBox puede devolverle la contraseña ingresada como SecureString directamente:

Propiedad PasswordBox.SecurePassword

Obtiene la contraseña que actualmente posee PasswordBox como SecureString .

Esto es útil porque en todas partes donde solía pasar una cadena sin formato, ahora tiene el sistema de tipos quejándose de que SecureString es incompatible con String. Desea ir el mayor tiempo posible antes de tener que convertir su SecureString nuevamente en una cadena normal.

Convertir un SecureString es bastante fácil:

  • SecureStringToBSTR
  • PtrToStringBSTR

como en:

private static string CreateString(SecureString secureString)
{
    IntPtr intPtr = IntPtr.Zero;
    if (secureString == null || secureString.Length == 0)
    {
        return string.Empty;
    }
    string result;
    try
    {
        intPtr = Marshal.SecureStringToBSTR(secureString);
        result = Marshal.PtrToStringBSTR(intPtr);
    }
    finally
    {
        if (intPtr != IntPtr.Zero)
        {
            Marshal.ZeroFreeBSTR(intPtr);
        }
    }
    return result;
}

Simplemente no quieren que lo hagas.

Pero, ¿cómo consigo una cadena en un SecureString? Bueno, lo que debes hacer es dejar de tener una contraseña en una cadena en primer lugar. Necesitabas tenerlo en otra cosa. Incluso unChar[] matriz sería útil.

Ahí es cuando puede agregar cada carácter y borrar el texto sin formato cuando haya terminado:

for (int i=0; i < PasswordArray.Length; i++)
{
   password.AppendChar(PasswordArray[i]);
   PasswordArray[i] = (Char)0;
}

Necesita su contraseña almacenada en alguna memoria que pueda borrar. Cárguelo en SecureString desde allí.


tl; dr: SecureString existe para proporcionar el equivalente de ZeroMemory .

Algunas personas no ven el punto de borrar la contraseña del usuario de la memoria cuando un dispositivo está bloqueado , o borrar las pulsaciones de teclas de la memoria después de autenticarse . Esas personas no usan SecureString.

Ian Boyd
fuente
14

Existen muy pocos escenarios en los que pueda utilizar de manera sensata SecureString en la versión actual de Framework. Realmente solo es útil para interactuar con API no administradas: puede ordenarlo usando Marshal.SecureStringToGlobalAllocUnicode.

Tan pronto como lo convierta a / de un System.String, ha vencido su propósito.

La muestra de MSDN genera SecureString un carácter a la vez desde la entrada de la consola y pasa la cadena segura a una API no administrada. Es bastante complicado y poco realista.

Es posible que las futuras versiones de .NET tengan más soporte para SecureString que lo hará más útil, por ejemplo:

  • SecureString Console.ReadLineSecure () o similar para leer la entrada de la consola en un SecureString sin todo el código complicado en la muestra.

  • Reemplazo de WinForms TextBox que almacena su propiedad TextBox.Text como una cadena segura para que las contraseñas se puedan ingresar de forma segura.

  • Extensiones a las API relacionadas con la seguridad para permitir que las contraseñas se pasen como SecureString.

Sin lo anterior, SecureString tendrá un valor limitado.

Joe
fuente
12

Creo que la razón por la que tiene que agregar caracteres en lugar de una instanciación plana es porque en el fondo, pasar "contraseña" al constructor de SecureString coloca esa cadena de "contraseña" en la memoria, lo que anula el propósito de la cadena segura.

Al agregar, solo está poniendo un carácter a la vez en la memoria, lo que probablemente no sea adyacente físicamente, lo que hace que sea mucho más difícil reconstruir la cadena original. Podría estar equivocado aquí, pero así fue como me lo explicaron.

El propósito de la clase es evitar que los datos seguros se expongan a través de un volcado de memoria o una herramienta similar.

JoshReedSchramm
fuente
11

MS descubrió que en ciertas instancias de causar que el servidor (escritorio, lo que sea) se bloquee, hubo momentos en que el entorno de ejecución haría un volcado de memoria exponiendo el contenido de lo que hay en la memoria. Secure String lo cifra en la memoria para evitar que el atacante pueda recuperar el contenido de la cadena.

kemiller2002
fuente
5

Uno de los grandes beneficios de un SecureString es que se supone que evita la posibilidad de que sus datos se almacenen en el disco debido al almacenamiento en caché de la página. Si tiene una contraseña en la memoria y luego carga un gran programa o conjunto de datos, su contraseña puede escribirse en el archivo de intercambio a medida que su programa se borra de la memoria. Con SecureString, al menos los datos no se quedarán indefinidamente en su disco en texto claro.

Jason Z
fuente
4

Supongo que es porque la cadena está destinada a ser segura, es decir, un hacker no debería poder leerla. Si lo inicializa con una cadena, el hacker podría leer la cadena original.

OregonGhost
fuente
4

Bueno, como dice la descripción, el valor se almacena cifrado, lo que significa que un volcado de memoria de su proceso no revelará el valor de la cadena (sin un trabajo bastante serio).

La razón por la que no puede construir un SecureString a partir de una cadena constante es porque tendría una versión sin cifrar de la cadena en la memoria. Limitarlo a crear la cadena en pedazos reduce el riesgo de tener toda la cadena en la memoria a la vez.

Mark Bessey
fuente
2
Si están limitando la construcción de una cadena constante, entonces la línea foreach (char c en "contraseña". ToCharArray ()) vencería eso, ¿no? Debe ser pass.AppendChar ('p'); pass.AppendChar ('a'); etc?
Richard Morgan
Sí, puede tirar fácilmente la poca protección que SecureString le brinda. Están tratando de dificultar que te dispares por completo en el pie. Obviamente tiene que haber alguna forma de obtener el valor dentro y fuera de un SecureString, o no podría usarlo para nada.
Mark Bessey
1

Otro caso de uso es cuando está trabajando con aplicaciones de pago (POS) y simplemente no puede usar estructuras de datos inmutables para almacenar datos confidenciales porque es un desarrollador cuidadoso. Por ejemplo: si voy a almacenar datos confidenciales de la tarjeta o metadatos de autorización en una cadena inmutable, siempre sería el caso cuando estos datos estén disponibles en la memoria durante un período de tiempo significativo después de que se descartaron. No puedo simplemente sobrescribirlo. Otra gran ventaja es que dichos datos confidenciales se guardan en la memoria encriptada.

romano
fuente