0

For a C# console app, I need to persist a password in the application settings but when I create a setting of type System.Security.SecureString the setting itself is removed from the plain-text config file. Since I can't see the raw value anymore I can't validate whether or not the data is still encrypted when saved.

Is SecureString the best approach or should I use ProtectedData to simply encrypt the string?

--EDIT-- Here is the test code that I used to validate that a SecureString can be persisted.

        [global::System.Configuration.ApplicationScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        public global::System.Security.SecureString Password
        {
            get
            {
                return ((global::System.Security.SecureString)(this["Password"]));
            }
            set { this["Password"] = value; }
        }

        static void Main(string[] args)
        {
            PersistPassword("A-Test-Password");
            Console.WriteLine(ReadPassword());
            Console.ReadLine();
        }

        static void PersistPassword(string Password)
        {
            SecureString ss = new SecureString();
            Password.ToCharArray().ToList().ForEach(ss.AppendChar);
            Settings.Default.Password = ss;
        }

        static string ReadPassword()
        {
            SecureString ss = Settings.Default.Password;
            IntPtr ptr = Marshal.SecureStringToCoTaskMemUnicode(ss);
            return Marshal.PtrToStringUni(ptr);
        }
Brian Mitchell
  • 849
  • 1
  • 8
  • 24
  • 1
    I don't think that will work at all. – SLaks Sep 04 '15 at 17:32
  • You can use `BCrypt` to hash your password. Secure string is just a class and it doesn't allow you to work with a string actually you can only work with it using `Marshal` and `Pointer` – Fabjan Sep 04 '15 at 17:39
  • If you would like to save the password in the app.config and use it from the application, I think you can do it. If so, please give a note, I will add some sample code. – Infinity Challenger Sep 04 '15 at 18:12
  • 1
    I can successfully persist and read a SecureString in a settings file and since it's doesn't give an exception I can only assume it's remaining encrypted (or at least serialized). – Brian Mitchell Sep 04 '15 at 18:17
  • 1
    [The SecureString never actually gets saved to disk](https://social.msdn.microsoft.com/Forums/en-US/d4557d9b-6e7d-4695-bfd0-a22a08e06160/). It reads back from within the same process because it's still in memory, but if you exit and restart, you'll find that the string is empty. – Raymond Chen Sep 05 '15 at 00:32
  • Thanks Raymond, you're exactly right! I never exited the program and when I tested it, sure enough, the value was null. – Brian Mitchell Sep 05 '15 at 05:15

2 Answers2

1

As states in MSDN,

The value of an instance of SecureString is automatically protected using a mechanism supported by the underlying platform when the instance is initialized or when the value is modified.

If you want to provide the mechanism to keep it readonly once the password in store in securestring then you can invoke the method MarkAsReadonly() on it.

For persistence purpose you can also Hash the SecureString and create a salt for it. You can retreive the salt for later use e.g. comparison pupose. Check out this code snippet which is using the salt over Securestring.

Community
  • 1
  • 1
vendettamit
  • 14,315
  • 2
  • 32
  • 54
1

You cannot persist data that is encrypted with SecureString. The key is held in memory, and only lasts as long as your program is alive. SecureString is a wrapper around the native CryptProtectMemory function.

If you need you need the encrypted data to be persistable (for longer than your program exists), you need the Data Protection API (DPAPI), and it's CryptProtectData function - which is exposed to C# users through the ProtectedData class.

SecureString has the advantage of being ephemeral; useful for:

  • passwords
  • credit card numbers
  • Social Insurance numbers

while they are being used by your program - and then deleted.

The DPAPI is better for long-term storage. But beware the protection levels, and you choose the one that is appropriate for what you need:

  • only decryptable by me
  • only decryptable on this PC
  • only decryptable by anyone on the domain

If you need encryption that can survive transport to different sites or different domains: CryptProtectData is not for you.

Four levels

  • CryptProtectMemory (SecureString in .NET): only useful in memory of your process
  • CryptProtectData (ProtectedData in .NET): encrypts a blob of data, and you can store it anywhere you like (memory, registry, hard disk) - but you have to store it yourself.
  • CredWrite: encrypts a password using CryptProtectData, and stores it for you in the Windows Password Vault (Start → Credential Manager)
  • CredUIPromptForCredentials/CredUIConfirmCredentials: prompt the user for a password, encrypt it, and saves it in the Password Vault (Start → Credential Manager) using CredWrite
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
  • Whilst this for C#, I'm not sure this is really true as SecureString's use in PowerShell can be dumped, in it's encrypted form, and stored persistently. – Vjz May 29 '23 at 23:53