Encrypting Passwords in a .NET app.config File

I've been contributing to the Witty project lately. I'm a fan of Twitter, and it's nice to work on a popular WPF application with some hotshot coders including a WPF pro like Alan Le. Lately, I noticed that we were storing the user's password in plaintext application config file:

<setting name="Password" serializeAs="String"> 
    <value>OOPS-WE-STORED-THE-PASSWORD-IN-PLAINTEXT</value> 
</setting>

So, yeah, that's less than ideal. Foolishly, I volunteered to fix it. There's plenty of information on encrypting ASP.NET configuration settings in web.config files, but encrypting settings in a desktop application isn't as well documented. Here's what I came up with.

DPAPI, Papi!

The best way to encrypt configuration settings is with DPAPI, the Data Protection Application Programmer's Interface:

This Data Protection API (DPAPI) is a pair of function calls that provide OS-level data protection services to user and system processes. By OS-level, we mean a service that is provided by the operating system itself and does not require any additional libraries. By data protection, we mean a service that provides confidentiality of data through encryption. Since the data protection is part of the OS, every application can now secure data without needing any specific cryptographic code other than the necessary function calls to DPAPI.

That sounds pretty good. But is it secure? Let's ask old man Wikipedia:

The keys used for encrypting the user's keys are stored under "%USERPROFILE%\Application Data\Microsoft\Protect\{SID}", where {SID} is the security identifier of that user. The DPAPI key is stored in the same file as the master key that protects the users private keys. It usually is 40 bytes of random data. DPAPI doesn't store any persistent data for itself; instead, it simply receives plaintext and returns cryptext (or vice-versa).

DPAPI security relies upon the system's ability to protect the Master Key and RSA private keys from compromise, which in most attack scenarios is most highly reliant on the security of the end user's credentials. Particular data binary large objects can be encrypted in a way that salt is added and/or an external user-provided password (aka "Strong Key Protection") is required. The use of a salt is a per-implementation option - i.e. under the control of the application developer - not controllable by the end user or IT professional.

Yeah, I didn't read it either. I did check the footnotes and saw that nobody's bragging about yoinking data out of it, though. And it has to  be better than storing passwords in plaintext. So, awesome, let's go for it!

The Nuclear Option: Encrypt The Whole Thing

The easiest way to deal with the problem is to just encrypt the entire section. That's because the ConfigurationSection knows how to protect itself, like so:

protected override void OnStartup(StartupEventArgs e) 
{ 
    // Lots of other important stuff here... 
    EncryptConfigSection("userSettings/Witty.Properties.Settings"); 
    base.OnStartup(e); 
}

private void EncryptConfigSection(string sectionKey)
{
    Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
    ConfigurationSection section = config.GetSection(sectionKey);
    if (section != null)
    {
        if (!section.SectionInformation.IsProtected)
        {
            if (!section.ElementInformation.IsLocked)
            {
                section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
                section.SectionInformation.ForceSave = true;
                config.Save(ConfigurationSaveMode.Full);
            }
        }
    }
}

 

Once we've done that, the entire settings section is encrypted and placed in a <CipherValue> block:

<userSettings>
 <Witty.Properties.Settings configProtectionProvider="DataProtectionConfigurationProvider">
  <EncryptedData>
    <CipherData>
      <CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAABbLHX[...]</CipherValue>
    </CipherData>
  </EncryptedData>
 </Witty.Properties.Settings>
</userSettings>

That's great from a security standpoint, but by encrypting everything, we've unnecessarily restricted access to all the information in the configuration file.

<userSettings> 
<Witty.Properties.Settings> 
  <setting name="Username" serializeAs="String"> 
    <value>UserNameGoesHere</value> 
  </setting> 
  <setting name="Password" serializeAs="String"> 
    <value>OOPS-WE-STORED-THE-PASSWORD-IN-PLAINTEXT</value> 
  </setting> 
  <setting name="RefreshInterval" serializeAs="String"> 
    <value>5</value> 
  </setting> 
  <setting name="LastUpdated" serializeAs="String"> 
    <value /> 
  </setting> 
  <setting name="PlaySounds" serializeAs="String"> 
    <value>True</value> 
  </setting> 
  ... 
</Witty.Properties.Settings> 
</userSettings>

So, what would be better is to encrypt just the password. To do that, we'll need to look into SecureString and System.Security.Cryptography.ProtectedData.

What's With The SecureString?

Like just about everything to do with ASP.NET development, David Hayden told us everything we need to know about SecureString years ago. The short story is that a System.String hangs around in memory until the garbage collector picks it up, so even if we're encrypting passwords or other sensitive data in our configuration file, it's possible to snag them from memory if we're using a standard System.String. SecureString uses our old friend DPAPI to encrypt values, so they're safe from memory snooping.

It's not as great as it sounds, though, because few API's accept or return SecureStrings. While it's a good practice to use SecureStrings when we can, we'll have to convert to and from standard System.String values at some point. While we're looking at security, we might as well use SecureStrings when possible, but we should keep in mind the fact that it's totally futile. Well, not that bad, but there are times where the sensitive information is still stored as insecure strings in memory.

Encrypting Strings with ProtectedData

So here's the actual meat of this post - the code I used to encrypt passwords in Witty's configuration. We've got two main methods, EncryptString and DecryptString. They both call in to ToSecureString and ToUnsecureString (great name, huh?) whose purpose should be pretty self-explanatory.

static byte[] entropy = System.Text.Encoding.Unicode.GetBytes("Salt Is Not A Password");

public static string EncryptString(System.Security.SecureString input)
{
    byte[] encryptedData = System.Security.Cryptography.ProtectedData.Protect(
        System.Text.Encoding.Unicode.GetBytes(ToInsecureString(input)),
        entropy,
        System.Security.Cryptography.DataProtectionScope.CurrentUser);
    return Convert.ToBase64String(encryptedData);
}

public static SecureString DecryptString(string encryptedData)
{
    try
    {
        byte[] decryptedData = System.Security.Cryptography.ProtectedData.Unprotect(
            Convert.FromBase64String(encryptedData),
            entropy,
            System.Security.Cryptography.DataProtectionScope.CurrentUser);
        return ToSecureString(System.Text.Encoding.Unicode.GetString(decryptedData));
    }
    catch
    {
        return new SecureString();
    }
}

public static SecureString ToSecureString(string input)
{
    SecureString secure = new SecureString();
    foreach (char c in input)
    {
        secure.AppendChar(c);
    }
    secure.MakeReadOnly();
    return secure;
}

public static string ToInsecureString(SecureString input)
{
    string returnValue = string.Empty;
    IntPtr ptr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(input);
    try
    {
        returnValue = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(ptr);
    }
    finally
    {
        System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(ptr);
    }
    return returnValue;
}

Then we're pretty much set. When we want to encrypt passwords for storage, we'll make a call like this:

AppSettings.Password = EncryptString(ToSecureString(PasswordTextBox.Password));

And we can get the password back out with this kind of thing:

SecureString password = DecryptString(AppSettings.Password)

The payoff is that our configuration looks like this:

<Witty.Properties.Settings>
    <setting name="Username" serializeAs="String">
        <value>jongalloway</value>
    </setting>
    <setting name="Password" serializeAs="String">
        <value>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAV[lots more stuff that's not my password]</value>
    </setting>
    <setting name="RefreshInterval" serializeAs="String">
        <value>5</value>
    </setting>
    <setting name="LastUpdated" serializeAs="String">
        <value>4/11/2008 12:10:33 AM</value>
    </setting>
</Witty.Properties.Settings>
For further study:
http://msdn2.microsoft.com/en-us/library/system.configuration.dpapiprotectedconfigurationprovider.aspx
http://www.codeproject.com/KB/security/ProtectedConfigWinApps.aspx
http://www.builderau.com.au/program/dotnet/soa/Encrypting-NET-configuration-files-through-code/0,339028399,339281837,00.htm
Published Sunday, April 13, 2008 2:20 AM by Jon Galloway

Comments

# Dew Drop - April 13, 2008 | Alvin Ashcraft's Morning Dew

Pingback from  Dew Drop - April 13, 2008 | Alvin Ashcraft's Morning Dew

# Encrypting Passwords in a .NET app.config File

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Sunday, April 13, 2008 11:06 AM by DotNetKicks.com

# re: Encrypting Passwords in a .NET app.config File

You should clear out the encryptedData and decryptedData byte arrays once you are done with them using Array.Clear(..).

Raj

Sunday, April 13, 2008 1:33 PM by rajbk

# Encrypting configuration information in .NET (web/windows)

Encrypting configuration information in .NET (web/windows)

Sunday, April 13, 2008 8:49 PM by .NET Developer Notes

# asp net programmer

Pingback from  asp net programmer

Monday, April 14, 2008 2:49 AM by asp net programmer

# Mind Gravy &raquo; Blog Archive &raquo; links for 2008-04-14

Pingback from  Mind Gravy  &raquo; Blog Archive   &raquo; links for 2008-04-14

Monday, April 14, 2008 8:41 AM by Mind Gravy » Blog Archive » links for 2008-04-14

# re: Encrypting Passwords in a .NET app.config File

The salt is presumably worthless, given the existence of Reflector. I suppose is has some worth, in that it adds a small layer of extra obfuscation.

And if a bad guy can get at the config file to read it, I wonder how hard it would be to run the DPAPI to decrypt it...

Is this more than security through obscurity? Genuine question to any security experts out there.

Monday, April 14, 2008 5:10 PM by commenter

# re: Encrypting Passwords in a .NET app.config File

<i>Is this more than security through obscurity? Genuine question to any security experts out there.</i>

Good question

Tuesday, April 15, 2008 2:38 AM by junko

# re: Encrypting Passwords in a .NET app.config File

IMO, this *is* security through obscurity. In addition one would use a key generation logic for the entropy (instead of a string) and use a code obfuscator to add an additional layer of obscurity.

Raj

Tuesday, April 15, 2008 10:02 AM by rajbk

# re: Encrypting Passwords in a .NET app.config File

If the badguy has access to your Windows account then this kind of protection is useless.

This is more of a protection against someone reading your config from another Windows account.  (as far as i can tell)

Also, the additional entropy feels more like an IV then a salt?

Tuesday, April 15, 2008 11:49 AM by JJ

# re: Encrypting Passwords in a .NET app.config File

@commenter I agree that the salt adds little to the security of the system, since it's required to the same on encryption and decryption. I'd still use it, since it would provide another level of protection over automated attacks.

Wednesday, April 16, 2008 12:20 PM by Jon Galloway

# re: Encrypting Passwords in a .NET app.config File

@junko, @rajbk, @JJ

If you're interested in the security of the system, please read the link I included.

msdn2.microsoft.com/.../ms995355.aspx

I joked about it because I didn't want to get into it in detail, but it is much more than security by obscurity, and it's not useless if an attacker has access to your Windows account.

For instance, encryption uses published standard encryption algorithms like Triple-DES that provide a level of security which is more than sufficient for most applications. Also, DPAPI uses the user's password, so an attacker would need both authenticated access to my filesystem and my system password to access my data. It's hard to expect that the system should prevent access to an attacker with both filesystem access and my account password, right?

Wednesday, April 16, 2008 12:40 PM by Jon Galloway

# storing data

Pingback from  storing data

Thursday, April 17, 2008 9:03 PM by storing data

# re: Encrypting Passwords in a .NET app.config File

Is someone had your system password, I think it wouldn't be to hard for him to get authenticated access to your filesystem :)

Friday, May 02, 2008 3:18 PM by Tibon

# asp net programmer

Pingback from  asp net programmer

Friday, May 09, 2008 2:24 AM by asp net programmer

# Encrypting Passwords in a .NET app.config File &laquo; Rams On It - .NET

Pingback from  Encrypting Passwords in a .NET app.config File &laquo; Rams On It - .NET

# convert html to wikipedia

Pingback from  convert html to wikipedia

Tuesday, May 13, 2008 7:17 AM by convert html to wikipedia

# Encrypting Passwords in a .NET app.config File

Your Story is Submitted - Trackback from DotNetShoutout

Thursday, November 20, 2008 7:49 AM by DotNetShoutout

# re: Encrypting Passwords in a .NET app.config File

Looks a nice solutions.

If only there were some way to do this on our legacy Solaris boxes.

Thursday, January 22, 2009 1:45 PM by RussellH

# re: Encrypting Passwords in a .NET app.config File

woow! dude, thats it a great post thanks for it

Saturday, March 07, 2009 4:49 PM by Jimmy

# re: Encrypting Passwords in a .NET app.config File

Cool, but you should note that the project may require adding a .NET reference to System.Security.dll before the enums (such as DataProtectionScope) will resolve correctly.

Wednesday, April 01, 2009 11:57 AM by MV10

# re: Encrypting Passwords in a .NET app.config File

Very nice this is something I wanted to do with my app.config.

I did notice that you can't read the SecureString on decrypt?

SecureString password = DecryptString(AppSettings.Password)

I did find so info on Marshal.SecureStringToBSTR which will convert it back to regular string after decryption?

Is this a good method or is there another way to decrypt back to a readable string?

Thanks

Monday, May 25, 2009 11:21 AM by David Welford-Costelloe

# re: Encrypting Passwords in a .NET app.config File

Sorru forgot to add the link to MSDN:

msdn.microsoft.com/.../system.runtime.interopservices.marshal(VS.71).aspx

Monday, May 25, 2009 11:25 AM by David Welford-Costelloe

# re: Encrypting Passwords in a .NET app.config File

I'm pretty new to VB.NET and I'm working on a desktop application. I need to encrypt some items in the userSettings and applicationSettings sections of the app.config file.

I successfully converted the example code to VB, but I have a question about implementing the code: Where do I put it? I have one form in my app. Will it just go in the code behind for the form, or does the code need to go in its own class? And if that's the case, how do I access it from my form?

If not the author, could SOMEone please help me?

Thanks!

Thursday, May 28, 2009 12:47 PM by MarkY

# re: Encrypting Passwords in a .NET app.config File

Use appropriate members of the System.Runtime.InteropServices..::.Marshal class, such as the SecureStringToBSTR method, to manipulate the value of a SecureString object.

Tuesday, June 16, 2009 9:42 AM by rrvolk

# re: Encrypting Passwords in a .NET app.config File

You can see he already does that in the ToInsecureString() method. To get the readable string do this: string readable = ToInsecureString(password);

Tuesday, June 16, 2009 9:58 AM by rrvolk

# re: Encrypting Passwords in a .NET app.config File

I want to encrypt connection string in general, can i use a remote function and use Runtime.Remoting to run the function from the server that opens a connection and then it will return the conection object?

Tuesday, August 11, 2009 6:52 AM by IBR

# re: Encrypting Passwords in a .NET app.config File

Just two small issues if it's not information set at runtime from the user such as a password, but pre-coded or preset value in the project's applications setting, such as connection strings:

- this secures the information in the app.config file, but, if you open the .exe file with notepad, the value will still be obvious in plain text.

- if the application is installed on the users machine, but not yet run, the information isn't encrypted yet and vulnerable.

Tuesday, October 20, 2009 1:05 PM by Brian

Leave a Comment

(required) 
(required) 
(optional)
(required)