When we are writing software for our clients, we have a fiduciary responsibility to ensure the security of their site.
One way to increase security is to ensure that passwords and other sensitive information are not laying around in our config files unsecured (in plain text). All too many times I have seen something like the following in a config file:
Code Sample:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="UserName" value="PresidentSkroob" />
<add key="Password" value="12345" />
</appSettings>
</configuration>
Fixing this problem is as easy as using Microsoft’s Data Protection API. Just encrypt the value by calling ProtectedData.Protect()
. And to decrypt the value, call ProtectedData.Unprotect()
.
Both methods take an entropy value (salt), and a DataProtectionScope
value that defines the scope for which the value should be encrypted. The scope may be one of the following: CurrentUser
– Only the current user can decrypt the value, or LocalMachine
– The value can only be decrypted on the local machine. CurrentUser
can be a little complicated since it may require you to impersonate users in certain cases, so I just use LocalMachine
. This does mean that you can’t ship the config file with the values pre-encrypted because they will not be decryptable on the machine its installed on (it can only be decrypted by the machine that encrypted it).
The functions return a byte array, so I Base64 encode the values prior to writing them to the config file. To do this, I wrote a few extension methods. Here they are:
Encryption:
/// <summary>
/// Converts a string to a protected value (encrypted using Data Protection API)
/// </summary>
/// <param name="value">The value.</param>
/// <param name="entropy">The entropy.</param>
/// <returns></returns>
public static string ToProtectedValue( this string value, string entropy )
{
var entropyBytes = Encoding.UTF8.GetBytes( entropy );
var valueBytes = Encoding.UTF8.GetBytes( value );
var securedBytes = ProtectedData.Protect( valueBytes,
entropyBytes,
DataProtectionScope.LocalMachine );
return securedBytes.AsBase64();
}
Decryption:
/// <summary>
/// Converts a string to a protected value (encrypted using Data Protection API)
/// </summary>
/// <param name="protectedString"></param>
/// <param name="entropy">The entropy.</param>
/// <returns></returns>
public static string ToUnprotectedString( this string protectedString, string entropy )
{
var entropyBytes = Encoding.UTF8.GetBytes( entropy );
var encryptedBytes = Convert.FromBase64String( protectedString );
var decryptedBytes = ProtectedData.Unprotect( encryptedBytes,
entropyBytes,
DataProtectionScope.LocalMachine );
return Encoding.UTF8.GetString( decryptedBytes );
}
After encrypting these values appropriately, your config file will look like this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="UserName" value="ADZgAAwAAAABAAAACLYg+TEHoFh88HcfvNTg0yAAAAAASAAACgAAAAEAAAAMFLpAQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA8BdWygYhjUWaw3teJQo04AQAAAACAAAAAAXFMCeBnOURIBAl2DJoQAAAAN7+sLiIZl+x9vPjC16wcmRQAAADS4d9bdlcLF9CmKrsbgGWPM29zAQ==" />
<add key="Password" value="AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA8BdWygYhjUWaw3teJQo04AQAAAACAAAAAAADZgAAwAAAABAAAAAhfu4ghotvmXphmGWOHFfbAAAAAASAAACgAAAAEAAAAMBMv0D9/mDwktrFSVNIPcwQAAAAwg2IDL1+MOHwdx2A1cax2RQAAACZLB62COjpupN2+pW4yT8LKR5Udg==" />
</appSettings>
</configuration>
Now you and your clients can sleep a little better at night knowing their sensitive information is just a bit more secure.