tl;dr: SecureString is a good and right idea. The reason Microsoft no longer recommends it is because .NET Core can't have it, because Linux doesn't support encryption.
Short Answer
- Use an Array of Char
- Encrypt it
Long Answer
There are two reasons you want SecureString.
The first is along the lines of the HeartBleed attack, and the reason SecureZeroMemory exists, and the reason Windows always zeros a page of memory before giving it to you: to avoid leaking information.
Bonus reading
Terminal Services keeps user passwords unencrypted in RAM.
The second, and the reason SecureString exists in .NET, is because you have no way to call SecureZeroMemory. Strings in .NET are immutable, and you don't get to control their lifetime.
So in order to solve the problem there are two elements:
- You can switch to an array of Char.
When it is an array, it means you can wipe the contents. This means you can do the moral equivalent of ZeroMemory
when you're done with the sensitive credit card number, bitcoin private key, password, Intel Blu-Ray Master Key: you can wipe them
That solves the problem of being completely unable to wipe a C# String
- Encrypt with
CryptProtectData
The other, unrelated, virtue of SecureString is that the raw strings will not appear in memory dumps, virtual machine RAM snapshots, web-server logs, debugger watch windows. Or in the case of the HeartBleed attack, will not appear in uninitialized data handed out to other web-site users.
These are the two core elements that SecureString provides.
And both you can replicate. The reason .NET Core doesn't have them is not because SecureString is a bad idea, or a wasted idea, or an incomplete idea, or "doesn't deliver what it promises". Instead it's because Linux doesn't have the equivalent of CryptProtectData
. And since .NET Core has to be cross-platform, they have to cater to the lowest common denominator. So they throw up their hands and say remove it.
But SecureString is just as valid as a concept as:
- ZeroMemory
- SecureZeroMemory
- Windows zero'ing out a page of RAM before giving it to your process
- why when you SetFileValidData, Windows zero's out the file contents
And anyone saying otherwise is flat out lying.
Reminder - you want to use SecureString
You want to use SecureString
Does it mean that the credit card number was in plaintext in memory at some point:
No, the reason is that it has very limited use and in most cases, the string either remains in memory, or re-appears. The original string remains in memory until it's GCd. And since SecureString has no counterpart in the Win32 API (or Linux) the original string will reappear once the app tries to do anything with it. Even in the SDK only NetworkCredentials used it properly and SecureString isn't a windows concept, so once you went down to a Windows API it was converted back.
- Yes, the Credit Card number was in plaintext in memory at some point.
- Yes, the user's bitlocker password was in memory at some point.
- Yes, the iOS user's PIN was in memory at some point.
- Yes, the BitCoin private key was in memory at some point.
- Yes, the data in the encrypted pagefile was unencrypted in RAM at some point.
But what we have to realize is:
- That doesn't mean you shouldn't wipe it from memory
- that doesn't mean you shouldn't prevent it from appearing in a logfile
- that doesn't mean you shouldn't prevent it from appearing in a swapfile
- that doesn't mean you shouldn't prevent it from appearing in a crash dump file
- that doesn't mean you shouldn't prevent it from appearing in a watch window
- that doesn't mean you shouldn't prevent it from appearing in a snapshot
These are good defense-in-depth measures.
And anyone saying differently is simply wrong.