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:
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.
SecureString
for new development: github.com/dotnet/platform-compat/blob/master/docs/DE0001.md – Habitation