Introduction
I think that at some point all ASP.NET developers are faced with the question of how to protect sensitive data stored in the web.config file. I recall a few years back in my first ASP.NET interview I was asked: "How would you secure a connection string containing passwords in the .config file?" I, maybe a little naively, replied that you can just put the connection string in there unencrypted as .config files are anyway not served by IIS.
Although the above statement is more or less correct you do however soon realize that this strategy simply won't do once the security conscious start tearing your code apart. In this article, I will attempt to recover from my "shameful" statement and will provide an easy to implement solution that is as close as possible to the Microsoft's best practice.
A common solution
A simple solution to the problem is to use .NET to encrypt the connection string stored in the .config file using an algorithm like the Triple DES or something similar. The application will read the encrypted value from the .config file and decrypt it again to retrieve the plain text.
This solution works very well and in this article I basically do exactly the same thing. The only problem with this approach is that you always end up with a key management issue. Just where do you store your decryption key to keep it safe? In this article, I will show how to keep the key reasonably safe using the Microsoft Data Protection API.
Impersonation
Commonly, we want to protect a username and password combination in the .config file which is used to impersonate a user with appropriate rights while trying to access SQL Server or a web service requiring authentication. In a situation where integrated Windows authentication can be used for accessing protected resources the following elegant solution can be implemented:
Add the following line in the <system.web>
section of your web.config file:
<identity impersonate="true" />
This line tells ASP.NET to impersonate the user who is accessing the site. If you turn off anonymous access for the application this will be the credentials that the user supplied when accessing the site. This is all you need to do if you know that all users will access the site using Integrated Windows authentication and that the user's credentials will have access to all the SQL Servers and web services that will be accessed by the application.
A better solution might however be to have a single user that will be used on behalf of the ASP.NET application to access the protected resources. In this situation simply allow anonymous access on the site and change the user account of the anonymous user to an account that will have the appropriate access to the protected resources.
Aspnet_setreg
Microsoft realized that we don't like storing unencrypted credentials in the web.config file and provided the aspnet_setreg utility to encrypt and store impersonation identity and the connection string for the session state in the registry under a secure key.
This implementation uses DPAPI to encrypt and decrypt the settings that should be secured. Please note that this implementation passes the CRYPTPROTECT_LOCAL_MACHINE
flag to CryptProtectData
and CryptUnprotectData
functions which means that Local Machine storage is used. You should therefore be aware that this is not the safest possible solution since anyone with local machine access will therefore potentially be able to decrypt these values. However what is used to make this implementation secure is a strong Discretionary Access Control List (DACL) that is applied to the registry key.
Using this utility is very simple, first download it from here and extract it to a location convenient for you. Once extracted run the application and supply it with a registry location and the username and password that you would like your application to impersonate:
aspnet_setreg.exe -k:SOFTWARE\MY_SECURE_APP\identity
-u:"yourdomainname\username" -p:"password"
After executing this command you will receive an output similar to that given below that will instruct you how to use the key and secure it.
Please edit your configuration to contain the following:
userName=
"registry:HKLM\SOFTWARE\MY_SECURE_APP\identity\ASPNET_SETREG,
userName"
password=
"registry:HKLM\SOFTWARE\MY_SECURE_APP\identity\ASPNET_SETREG,
password"
The DACL on the registry key grants Full Control to System,
Administrators, and Creator Owner.
If you have encrypted credentials for the <IDENTITY />
configuration section, or a connection string for the <sessionState/>
configuration section, ensure that the process identity has
Read access to the registry key. Furthermore, if you have
configured IIS to access content on a UNC share, the account used
to access the share will need Read access to the registry key.
Regedt32.exe may be used to view/modify registry key permissions.
You may rename the registry subkey and registry value in order to
prevent discovery.
The next step that must be performed is setting up the DACL for the registry key to allow ASP.NET to read it:
Finally, you can add the impersonation line to your web.config that uses the values you received in the previous step. Also ensure that all your connection strings use integrated Windows security.
<identity impersonate="true"
userName=
"registry:HKLM\SOFTWARE\MY_SECURE_APP\identity\ASPNET_SETREG,
userName"
password=
"registry:HKLM\SOFTWARE\MY_SECURE_APP\identity\ASPNET_SETREG,
password" />
Currently, this approach seems to be the Microsoft recommended best practice, but unfortunately this security mechanism is very limited in terms of the fact that it only allows you to securely store credentials for impersonation and Windows Integrated security. What is sometimes needed is a more generic approach that will allow you to store any sensitive encrypted data in the web.config file.
The solution that I propose to this problem is a combination of the approach just discussed and the encrypted strings approach. My approach assumes that the way credentials are stored in the registry and protected by the DACL is indeed the most secure protection offered to us by Microsoft.
DPAPI and Triple DES
The idea behind my solution is very simple, use the DPAPI and a DACL in exactly the same way as aspnet_setreg.exe does to store a master key in the registry that will be used to encrypt and decrypt sensitive data. This secure master key OS is then used with the Triple DES algorithm to encrypt and decrypt the values in the .config.
The first step is to store the master key in the registry, simply run the Windows application executable, Foulds.Security.UserInterface.DataProtection.exe, and use it to configure the registry key location and store it in the registry. After clicking on the Save
button click on the Load
button to ensure that the key can be loaded from the registry.
After the master key is stored in the database you can encrypt the values that should be stored in the web.config file. Enter the value in the plain text box and click encrypt, the result in the Cipher Text box can then be copied to the registry.
As with the aspnet_setreg utility you will have to configure the DACL for the registry key you have created to allow ASP.NET to read it.
A special section handler has been created to simplify the encryption and decryption of sensitive data. To enable this section handler add a reference to the Foulds.Security.dll
assembly in your web project.
Next add the section handler to your web.config file:
<configSections>
<sectionGroup name="fouldsSettings">
<section name="encrypted"
type=
"Foulds.Security.SectionHandlers.DataProtection.SectionHandler,
Foulds.Security"/>
</sectionGroup>
</configSections>
You then have to add the configuration section to your web.config file that will specify the location of master key in the registry and the encrypted key value pairs for your application. Below is a sample of the configuration section:
<fouldsSettings>
<encrypted registryKey="SOFTWARE\HannesFoulds\encryption,
masterkey">
<add key="secret1" value="aVvjoiS42p8=" />
<add key="secret2" value="mWMSMkNFLSM=" />
<add key="secret3" value="xwc3jnQ9Zfw=" />
</encrypted>
</fouldsSettings>
When you want to get the unencrypted value from this configuration section you can use the provided helper class in the Foulds.Security
assembly like this:
[Editor Note: Line breaks used to avoid scrolling]
string secret2 =
Foulds.Security.SectionHandlers.DataProtection.
ConfigSettings.Encrypted["secret2"];
Conclusion
This solution is by far the best way I have come up with to protect sensitive data in the web.config. Please feel free to scrutinize my code and help me make it better. Also if you would like me to flesh out this article with more technical details let me know, it's Friday afternoon now and I'm planning to go home soon ;-)