Pregunta

Estoy tratando de asimilar el propósito de SecureString de .NET. Desde 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.

     

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 lugar de

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

¿Qué aspecto de SecureString me estoy perdiendo?

¿Fue útil?

Solución

Algunas partes del marco que actualmente usan SecureString :

El objetivo principal es reducir la superficie de ataque, en lugar de eliminarla. SecureStrings son " ; anclado " en 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 el clave simétrica utilizada para cifrarla y descifrarla.

Como otros han dicho, la razón por la que tiene 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?

SecureString s 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 nuevamente en cadenas regulares para utilizarlos, 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.

Otros consejos

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ágina)
  • fijación en memoria
  • capacidad de marcar como de 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.

Con base en 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 invalida el propósito de SecureString.

Información adicional:

  • Una publicación de .NET Security blog hablando de lo mismo que cubierto aquí.
  • Y otro uno volviendo a visitarlo y mencionando una herramienta que PUEDE volcar el contenido de la 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.

Short Answer

why can't I just say:

SecureString password = new SecureString("password");

Because now you have password in memory; with no way to wipe it - which is exactly the point of SecureString.

Long Answer

The reason SecureString exists is because you cannot use ZeroMemory to wipe sensitive data when you're done with it. It exists to solve an issue that exists because of the CLR.

In a regular native application you would call SecureZeroMemory:

Fills a block of memory with zeros.

Note: SecureZeroMemory is is identical to ZeroMemory, except the compiler won't optimize it away.

The problem is that you can't call ZeroMemory or SecureZeroMemory inside .NET. And in .NET strings are immutable; you can't even overwrite the contents of the string like you can do in other languages:

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

So what can you do? How do we provide the ability in .NET to wipe a password, or credit card number from memory when we're done with it?

The only way it can be done would be to place the string in some native memory block, where you can then call ZeroMemory. A native memory object such as:

  • a BSTR
  • an HGLOBAL
  • CoTaskMem unmanaged memory

SecureString gives the lost ability back

In .NET, Strings cannot be wiped when you are done with them:

  • they are immutable; you cannot overwrite their contents
  • you cannot Dispose of them
  • their cleanup is at the mercy of the garbage collector

SecureString exists as a way to pass around strings safety, and be able to guarantee their cleanup when you need to.

You asked the question:

why can't I just say:

SecureString password = new SecureString("password");

Because now you have password in memory; with no way to wipe it. It's stuck there until the CLR happens to decide to re-use that memory. You've put us right back where we started; a running application with a password we can't get rid of, and where a memory dump (or Process Monitor) can see the password.

SecureString uses the Data Protection API to store the string encrypted in memory; that way the string will not exist in swapfiles, crash dumps, or even in the local variables window with a colleague looking over your should.

How do i read the password?

Then is the question: how do i interact with the string? You absolutely don't want a method like:

String connectionString = secureConnectionString.ToString()

because now you're right back where you started - a password you cannot get rid of. You want to force developers to handle the sensitive string correctly - so that it can be wiped from memory.

That is why .NET provides three handy helper functions to marshall a SecureString into a unmanaged memory:

You convert the string into an unmanaged memory blob, handle it, and then wipe it again.

Some APIs accept SecureStrings. For example in ADO.net 4.5 the SqlConnection.Credential takes a set SqlCredential:

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

You can also change the password within a Connection String:

SqlConnection.ChangePassword(connectionString, cred, newPassword);

And there are a lot of places inside .NET where they continue to accept a plain String for compatibility purposes, then quickly turn around an put it into a SecureString.

How to put text into the SecureString?

This still leaves the problem:

How do i get a password into the SecureString in the first place?

This is the challenge, but the point is to get you thinking about security.

Sometimes the functionality is already provided for you. For example, the WPF PasswordBox control can return you the entered password as a SecureString directly:

PasswordBox.SecurePassword Property

Gets the password currently held by the PasswordBox as a SecureString.

This is helpful because everywhere you used to pass around a raw string, you now have the type system complaining that SecureString is incompatible with String. You want to go as long as possible before having to convert your SecureString back into regular string.

Converting a SecureString is easy enough:

  • SecureStringToBSTR
  • PtrToStringBSTR

as in:

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

They just really don't want you doing it.

But how do i get a string into a SecureString? Well what you need to do is stop having a password in a String in the first place. You needed to have it in something else. Even a Char[] array would be helpful.

That's when you can append each character and wipe the plaintext when you're done:

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

You need your password stored in some memory that you can wipe. Load it into the SecureString from there.


tl;dr: SecureString exists to provide the equivalent of ZeroMemory.

Some people don't see the point in wiping the user's password from memory when a device is locked, or wiping wiping keystrokes from memory after they'authenticated. Those people do not use SecureString.

There are very few scenarios where you can sensibly use SecureString in the current version of the Framework. It's really only useful for interacting with unmanaged APIs - you can marshal it using Marshal.SecureStringToGlobalAllocUnicode.

As soon as you convert it to/from a System.String, you've defeated its purpose.

The MSDN sample generates the SecureString a character at a time from console input and passes the secure string to an unmanaged API. It's rather convoluted and unrealistic.

You might expect future versions of .NET to have more support for SecureString that will make it more useful, e.g.:

  • SecureString Console.ReadLineSecure() or similar to read console input into a SecureString without all the convoluted code in the sample.

  • WinForms TextBox replacement that stores its TextBox.Text property as a secure string so that passwords can be entered securely.

  • Extensions to security-related APIs to allow passwords to be passed as SecureString.

Without the above, SecureString will be of limited value.

I believe the reason why you have to do character appending instead of one flat instantiation is because in the background passing "password" to the constructor of SecureString puts that "password" string in memory defeating the purpose of secure string.

By appending you are only putting a character at a time into memory which is likley not to be adjacent to each other physically making it much harder to reconstruct the original string. I could be wrong here but that's how it was explained to me.

The purpose of the class is to prevent secure data from being exposed via a memory dump or similar tool.

MS found that on certain instances of causing the server (desktop, whatever) to crash there were times when the runtime environment would do a memory dump exposing the contents of what's in memory. Secure String encrypts it in memory to prevent the attacker from being able to retrieve the contents of the string.

One of the big benefits of a SecureString is that it is supposed avoid the possibility of your data being stored to disk due to page caching. If you have a password in memory and then load a large program or data set, your password may get written to the swap file as your program is paged out of memory. With a SecureString, at least the data will not be sitting around indefinitely on your disk in clear text.

I guess it's because the string is meant to be secure, i.e. a hacker should not be able to read it. If you initialize it with a string, the hacker could read the original string.

Well, as the description states, the value is stored encrypted, with means that a memory dump of your process won't reveal the string's value (without some fairly serious work).

The reason you can't just construct a SecureString from a constant string is because then you would have an unencrypted version of the string in memory. Limiting you to creating the string in pieces reduces the risk of having the whole string in memory at once.

I would stop using SecureString . Looks like PG guys are dropping support for it. Possibly even pull it in the future - https://github.com/dotnet/apireviews/tree/master/2015-07-14-securestring .

We should remove encryption from SecureString across all platforms in .NET Core - We should obsolete SecureString - We probably shouldn't expose SecureString in .NET Core

Another use case is when you are working with payment applications (POS) and you simply can't use immutable data structures in order to store sensitive data because you are careful developer. For instance: if I will store sensitive card data or authorisation metadata into immutable string there always would be the case when this data will be available in memory for significant amount of time after it was discarded. I cannot simply overwrite it. Another huge advantage where such sensitive data being kept in memory encrypted.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top