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:

<identityimpersonate="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:

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.

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.

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:

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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Comments and Discussions

First, good article and thank you for sharing it. I have been search for a few weeks now to find the best way to handle this. I think your approach is suitable for our needs, but I have a question regarding the implementation of this. I understand that your utility stores the master key in the registry and is doing this by using DPAPI. However, is it storing it in the Machine Store or the User Store?

Also, I would assume the utility would have to be run on the IIS server that will be hosting the application. The problem I have with DPAPI is when your application is in a development/test/production environment. Suppose that you develop on one IIS server and DB server, then copy the application to a different test IIS and DB server, and then copy it to yet another production IIS and DB server. Depending on which “environment” your application is running in, you will be connecting to a different database server. Obviously on each IIS server, you would have to run this utility to generate the appropriate key and encrypted value. Once you have done this on all IIS servers, I suppose you could have all three encrypted connection strings in your .config file and just comment/uncomment the appropriate one. What are your thoughts on this?

Thank you for your comments on the article. The DPAPI is encrypted for storage in the registry in the same way that the aspnet_setreg utility does - with Machine Store. If you had read up on the DPAPI you will immediately start to think that that can’t possibly the most secure since any user that has local access to the machine will be able to decrypt these values. This is indeed true, and this is exactly why I keep stressing the importance of your DACL of your registry key.

Logically the next question will be why I don’t simply change my application to use User Store for encrypting/decrypting the master key in the registry. I believe that the reason that Microsoft don’t do it in their implementation (and therefore I don’t either) is that it is very problematic using DPAPI with User Store in an ASP.NET application as the account under which ASP.NET runs does not load a user profile that is needed for User Store.

Microsoft has provided us with a way the encrypt/decrypt values with DPAPI using the User Store, you can view this article at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secmod/html/secmod23.asp

To brutally simply things: In the solution that this article provides you have a COM+ Component (Enterprise Service) that runs under a specific preset user account and the ASP.NET application then calls on this Enterprise Service to provide the encryption/decryption services. To begin with I think that this solution can be a pain to implement for ASP.NET applications as you need to install and configure the COM+ Component and run it under a specific account, and then also install and configure a Windows Service under a specific account to initialize the COM+ Component. Besides installation being a pain in the butt, which we can perhaps live with for the sake of security, the reason why I chose not to use this is because I think it is pointless, my logic goes like this:

My motivation for wanting to use User Store is to provide additional security to ensure that nobody except the user that runs your ASP.NET application can decrypt the master key. If you want to employ User Store from your ASP.NET application you will have to do this through a COM+ Component that always runs under a preset account. In my view you therefore just added additional points of failure to your application (the windows service, and the enterprise service) and are left with exactly the same problem you tried to fix – Now despite the fact that you are using User Store ANYBODY that has access to your machine will STILL be able to decrypt your master key simply by using the Enterprise Service!!

I would like to just explicitly state that the above is my interpretation of things and if it is wrong I really would like to get feedback.

To answer your second question (sorry for beating about the bush a little, just trying to clarify the article a little):
You are correct in stating that the Windows Forms utility will have to be run on each server that will host your application in order for the master key to be placed in the registry. Because Machine Store is used the cipher text for the master key will be unique for each server, so this value cannot be copied to another server and have to be re-created for each of your application servers that host the application– this is also why I don’t use the DPAPI to encrypt the values stored in the web.config. If the values in the web.config were encrypted using DPAPI then you also could not have copied the web.config file to another server since it would not have been possible to decrypt the values on any other server. But since I chose to encrypt the values in the web.config file with Triple DES you can simply copy your web.config file and the server will be able to decrypt these values as long as you use the utility to store the same master key in the registry.

The key/value pair management issue you are referring to when deploying form for example staging to live with different SQL Connection strings is the same you are faced with if you were not using my section handler – I haven’t really come up with a clever solution to this so in my applications I would also comment/uncomment the appropriate values as you suggest. If you do however know beforehand what your connection strings will be in your different environments you can create the encrypted strings for them on your development environment if you are going to use the same master key across your deployments.

I hope my mad ramblings made some sense to you and answered your questions, please let me know if you need more clarification, and I would love to hear your opinion on all this!

This is a great topic and one that a lot of people have tackled in different ways. It always puzzled me that Microsoft never had a straightforward solution for this considering how widespread a problem it is. Even the DPAPI solution seems inelegant to me due to deployment concerns.

Yes, I understand how your approach is more deployable but I hate using the registry for anything as critical as a decryption key...DACL or no.

I actually came up with my own easily deployable solution that does not use the registry or DPAPI or anything else machine specific for that matter. It adopts the "hide in plain sight" approach and uses a clever algorithm to regenerate the key at runtime when needed. Unfortunately, I can't really publish this solution because its main weakness is that if hackers understand it and know you are using it, their job becomes much easier.

But let me hint at it by just saying this: What if you had a very large chunk of text data and your key was composed of certain specific bytes from that chunk? First they would have to figure out what data you were using, and even if they knew that, it would be a big challenge to figure out which ones you are using, particularly if that chunk is very large.

You've overlooked one thing with using the DPAPI userstore and COM+ component. You can restrict who has access to your COM+ component using the "Enforce access checks for this application". This allows you to restrict access to the COM+ component to just the user that your ASP.NET application is running under. This is similar to how your using ACL's to restrict access in the registry. The Microsoft article skips over this bit of security which is a shame as it's a pretty important part of the solution.