The Security Samurai

Necessity is the plea for every infringement of human freedom. It is the argument of tyrants; it is the creed of slaves - William Pitt

My Links

Post Categories

Archives


Random Family Guy Quotes

Blog Stats

.Where I Work

General Blogs I Read

Security Blogs I Read

Useful Articles

Encryption Key Storage - Data Protection API (DPAPI) User Store Without A Serviced Component

I made a post a last week on Block Ciphers and Initialization Vectors and got a couple of comments (literally a couple) that the information was helpful, so I’ve decided to extend the discussion to other subjects and eventually combine them into an article.  This post builds on the main scenario from the last post which was that of a server side application, where you are encrypting small fields of data in a database.  Now we will focus on the storage of our encryption key. 

Storing encryption keys falls underneath the category of key management and is usually the weakest link in any implementation.  For those of us that keep up with hacks, we see this presented all the time in commercial products that store the encryption key right next to the data that is being encrypted or when developers try to hide the key through obfuscation.  No method is bulletproof, but these two methods are incredibly weak.  As a joke, we refer to the data as being encrypted with Rot26

The Caesar Cipher uses character substitution, where each character is replaced by the character that is x places after it.  Rot13, or Rotate by 13 is a popular implementation of this and is in use in newsgroups and various other public forums.  2Rot13 or Rot26, rotates each character by 13 places twice or by 26 places, effectively making the ciphertext equal to the plaintext.

About a year and a half ago I worked on a project where we had to store an encryption key in ASP.NET using the Data Protection API (DPAPI) and I did a quick search to find other articles that accomplish this.  Imagine my surprise when I find that every resource out there tells me that what I did can’t be done!  Here are examples of what I am referring to:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/SecNetHT08.asp

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secmod/html/secmod23.asp

The DPAPI is extremely simple to use and has been available since Windows 2000.  Data can be encrypted at the machine or user level and is done so with the TripleDES algorithm.  Encrypting data at the user level is the optimal approach.  Data encrypted at the machine level can be read by anything allowed to run on the machine while data encrypted at the user level can only be read by a process running as a particular user.  In our scenario where we are storing encrypted data in the database, if someone has managed to steal that database, then there is a good chance they will be able to run code on the machine storing the key.  If we were to use the machine store, we would add a tiny amount of complexity to the hack, but no real security.  It’s a different matter entirely for them to logon as a specific account to access a particular user store.

In order for the DPAPI to work with a user store, that user must have a profile for key management (the answer to why can be found here).  The local ASPNET system account does not have a profile and is why those articles say you can’t use the DPAPI at the user level.  Incredibly, their solution is to use a serviced component that runs under a specific account, rather than changing ASP.NET to run under a different account. 

This struck me as odd, so I created a sample app to test that this could be done directly from ASP.NET using a properly configured user account (i.e. not an admin account but one that is only a member of the users group, has the right to log on as a service, write access to temp files that need to be generated by ASP.NET, etc.).  It worked just fine, as I expected.  I do not have access to a domain this week and was only able to test this in on a single machine, so if anyone wants to test this on their domain, ping me and I will send you the source code and instructions.  It takes about 15 minutes to setup and run the test.  I have done this before on a domain, the concept should be the same on the local machine as a domain, but with all the popular advice out there saying you can’t, I’d feel better after this test has been run on a machine that’s part of a domain.

I don’t know why MS suggests using a serviced component instead of configuring a user account for ASP.NET, but I really don’t like it.  Not only does it add additional components that need to be developed, installed, and maintained, but you are also adding additional attack vectors.  Remember, our attacker already stole our database and just needs the key.  This is the scenario we are guarding against.  Now the key can be stolen through a serviced component and an additional user account.  Securing these two additional items is not trivial and significantly weakens the implementation.

As a matter of practice, we all should be using a domain account for the ASP.NET Worker Processes to execute under anyway.  This gives us an easy, secure way to authorize access to the various resources our application needs to interact with.  Instead of worrying about how to encrypt connection strings, we need to remember that the easiest way to protect a secret is not to have to store it in the first place.  This is 1 of the reasons why using Integrated Security between our applications and the database makes a solution much more secure.  I know some folks out there are forced to store credentials and yes, if you do you should encrypt them, but those of you who are forced to store credentials because of an IT policy might want to focus on changing the policy rather than finding secure workarounds. 

Here are the steps to use the DPAPI with a user store from ASP.NET:

  1. Create a domain account for ASP.NET to run under.  A list of items that the account will need access to can be found here.  The only thing you should do differently is to not deny the account the ability to log on locally (you will need to do this later).  Make sure to configure the account with an especially strong password (50 chars, all cryptographically generated random characters) to prevent unauthorized logons and increase the strength of the key used by the DPAPI.  Also create a sub key in the registry for your app to store data in and configure an ACL so that only this account can access to it.
  2. Create a DPAPI library to perform the encryption and decryption. 
  3. Create an installer routine on a CD that contains your symmetric key (don’t transmit the key over the network or store it on any machine) and a WinForm app that will encrypt the key using the DPAPI with a user store and store it in the registry.
  4. Log in as the domain account for ASP.NET and place your installation CD into the web server.  Run the WinForm app.
  5. Log in as an admin and install your ASP.NET application.  Your app should now have the ability to read the encrypted value from the registry and decrypt it with the DPAPI.
  6. Store the installation CD in a fireproof safe in a secure, off-site location.  This is and should be your only copy of the encryption key should you ever need it.
  7. Most likely you wrote down the ASP.NET account’s password for use in the installation process.  This should now be destroyed.

Any questions?  Comments?

Edited to Add:  I am analyzing the last attack vector, which is what the aspnet_setreg.exe utility does to protect the credentials of the ASP.NET user account.  If these credentials that are stored in the registry are accessed, all this is for nothing.  I will post my findings later this week, but it is not looking good so far....

 

posted on Wednesday, June 15, 2005 2:22 PM