Passwords in Web.config?

Use DPAPI to Secure Database Passwords

Jeff Prosise

October 30, 2009

8 Min Read
ITPro Today logo in a gray background | ITPro Today

Q. Is it safe to put passwords and othersecrets in Web.config files?

RELATED: "Store Passwords Securely" and "ASP.NET Web Security: Protect User Passwords with Hashing and Salt"

A. In theory, yes. The section of Machine.config maps *.config files to an HTTPhandler named HttpForbiddenHandler, which fails requests and informs the userthat "this type of page is not served."

In practice, however, be careful.Hackers use various tricks to gain unauthorized access to files on Web servers.An ASP.NET Web server running an unpatched version of IIS 5.0, for example, isvulnerable to file disclosure hacks resulting from a bug in an ISAPI DLL namedISM.dll. Putting plaintext - that is, unencrypted - "secrets" in a Web.configfile on that server would be risky, at best.

To mitigate the effects ofsuccessful file disclosure attacks, many companies have policies in place thatforbid developers to place plaintext secrets in Config files. The reasoningbehind such policies is that multiple defenses are better than one; there's nosuch thing as being "too secure."

There are many ways to storesecrets more securely than in plaintext CONFIG files. One approach is toencrypt the secrets before placing them in Web.config. Another is to placeencrypted secrets in the registry and use access control lists (ACLs) torestrict access to them. I discuss both approaches at length in this column.

Encrypting data is easy using theclasses in the System.Security.Cryptography namespace, but here's thefly in the ointment: No matter which approach you choose, you must decide whereto store the decryption key. Storing it in a Config file is no good. If anattacker reads a Config file containing both an encrypted secret and thedecryption key, he or she can easily decipher the secret. Embedding thedecryption key in your code is little better because if the server iscompromised, the decryption key can be extracted from the raw or compiled code.

 

Using DPAPI

One convenient solution to theproblem of key storage is the Windows Data Protection API (DPAPI). The DPAPIincludes methods for encrypting and decrypting data. It also provides theencryption/decryption keys so you don't have to. In effect, this offloads theresponsibility for managing keys to the operating system, which istheoretically more secure than anything you could contrive yourself.

One problem DPAPI poses for ASP.NETprogrammers is that because it isn't wrapped in the .NET Framework ClassLibrary, you must use P/Invoke - that is, callouts to unmanaged code - to callit. Fortunately, Microsoft has already done most of the work for you. The MSDNdocument "How To: Create a DPAPI Library" (http://www.msdn.microsoft.com/library/en-us/dnnetsec/html/SecNetHT07.asp)contains the source code for a managed class named DataProtector that wraps the DPAPI. A related document, "How To:Use DPAPI (Machine Store) from ASP.NET" (http://www.msdn.microsoft.com/library/en-us/dnnetsec/html/SecNetHT08.asp),explains how to use the DataProtector class from ASP.NET.

These code samples demonstrate onetechnique for encrypting database connection strings and storing them securelyin Web.config using Microsoft's DataProtector class. The first containsthe source code for a command-line utility named Encrypt.exe that you can useto encrypt connection strings - or any other data, for that matter (see Figure1). The command:

encrypt server=  localhost;database=forums;uid=webuser;pwd=mxyzptlk

encrypts the indicated connectionstring and displays the result as a rather long base-64-encoded string. Observethat you don't provide an encryption key; the DPAPI provides it for you.

using System;using System.Text; class Encrypt{  static void Main (string[] args)  {    if (args.Length == 0)    {      Console.WriteLine("Syntax: Encrypt plaintext");      return;    }    DataProtector dp = new DataProtector(      DataProtector.Store.USE_MACHINE_STORE);    byte[] plaintext = Encoding.ASCII.GetBytes(args[0]);    Console.WriteLine(Convert.ToBase64String(dp.Encrypt(      plaintext, null)));  }}

Figure 1: Use this command-line utility to encrypt connectionstrings and other secrets.

The second example shows aWeb.config file containing an encrypted connection string (see Figure 2). Thestring is stored in the section of Web.config, where it caneasily be retrieved using Configuration.AppSettings.

        

Figure 2: ThisWeb.config file contains an encrypted connection string (the lines wrap forformatting purposes).

The final piece of the puzzle liesin the last bit of code, which contains the source code for a page that readsthe encrypted connection string from Web.config, decrypts it, and displays it(see Figure 3). Of course, in real life you wouldn't display the connectionstring; you'd use it to open a database connection. Again, you don't have toprovide a decryption key because DPAPI provides it for you.

         

Figure 3:Decrypt.aspx retrieves the encrypted connection string, decrypts it, anddisplays it.

Before you can run these samples,you must build an assembly containing the DataProtector class. Get thesource code from the URL I mentioned earlier and write it to a file namedDataProtect.cs. Then execute the following command in a Visual Studio.NETcommand prompt window to compile DataProtect.dll:

csc /t:library /unsafe+ dataprotect.cs

To use the assembly in ASP.NET,simply copy it to the bin subdirectory in the root directory of the hostapplication. To use it with Encrypt.exe (which dynamically links toDataProtect.dll), put a copy of DataProtect.dll in the same directory asEncrypt.exe, or, if you prefer, strong-name DataProtect.dll and install it inthe Global Assembly Cache. A compiled version of Encrypt.exe is included withthe files that accompany this article (see end of article for downloaddetails).

Does using DPAPI ensure thatsecrets are 100 percent secure? Of course not. But it raises the bar forattackers, and it relies on proven cryptographic devices such as Triple-DESencryption and PKCS5 key derivation. (For an excellent technical overview ofDPAPI, see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/windataprotection-dpapi.asp.)No scheme for storing secrets is perfect, but the one demonstrated here is farsafer than storing secrets in plaintext. And it strikes a reasonable balancebetween level of security and ease of implementation.

 

Using the Registry as Well

Should you decide that encryptingsecrets in Web.config isn't enough, or if your company enforces stringentsecurity policies that forbid even the placement of encrypted secrets in Configfiles, you can apply a double dose of security by both encrypting the secretsand putting them in the registry. The registry is outside the "Web space" ofIIS and is harder to hack than the file system - especially if you go the extramile and ACL-registry entries to restrict access to them.

To demonstrate, the REG file shownin Figure 4 creates a new key named HKEY_LOCAL_MACHINE/SOFTWARE/MyWebApp in theregistry and writes to it an encrypted connection string named"ConnectionString" - the same connection string used in the previous example.Figure 5 shows the new key and value as seen in RegEdt32.exe.

REGEDIT4[HKEY_LOCAL_MACHINESOFTWAREMyWebApp]"ConnectionString"="AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAUsAE5G7asEGKnwESpz+6cQQAAAACAAAAAAADZgAAqAAAABAAAAATEuHfkynIFAvWubbBboorAAAAAASAAACgAAAAEAAAAFJtFcgwkM4qDpkb5SQ/Mb9AAAAAyBSDtonw8GOCGmGpcWLYMYlVfXt1lTimcTYsv5+L0bJZoNDNLoJzTVKx+eoDTePZcik5Cj6+jOa1JG9rJ3R+TxQAAACxHb3MHN9ducLZ1hpX/S57YVVK/A=="

Figure 4: Run thisregistry script - Secret.reg - to place an encrypted connection string in theregistry under HKEY_LOCAL_MACHINE/SOFTWARE/MyWebApp (once more, lines wrap forformatting purposes).


Figure 5: This figure shows theencrypted connection string viewed in RegEdt32.exe.

While you're in RegEdt32.exe,right-click on the MyWebApp key and select Permissionsfrom the context menu. Now ACL the registry key for added protection bygranting full control to administrators and the SYSTEM account, and read accessto the account that ASP.NET runs as (see Figure 6). By default, ASP.NET runs asthe ASP.NET account on IIS 5.x, and the Network Service account on IIS 6.0.


Figure 6: Use RegEdt32.exe to ACLthe MyWebApp registry key, allowing read-only access to the ASP.NET workerprocess and full access to SYSTEM and administrators.

The Web page in Figure 7 reads theencrypted connection string from the registry, decrypts it, and displays it. Onthe outside, it's identical to Decrypt.aspx. On the inside, it's quitedifferent because the statement that reads the encrypted connection string fromWeb.config has been replaced with statements that read it from the registry.

As an experiment, temporarilyremove the ASP.NET account from the registry key's access control list andrefresh the page. You should see a security exception, because even ASP.NETcan't read the key. (If you don't see a security exception, that's trouble. Youmight be running ASP.NET as the SYSTEM account, which is strongly discouraged.)

<%@ Import Namespace="Microsoft.Win32" %>          

Figure 7:FetchAndDecrypt.aspx retrieves the encrypted connection string from theregistry, decrypts it, and displays it.

One drawback to storing secrets inACLed registry keys is that it complicates deployment. Instead of an XCOPYdeploy, you now have to run a script or setup program to create the registrykey and modify the key's ACL. You be the judge of whether the extraadministrative hassle is worth the added security.

 

Conclusion

A final note regarding the use ofDPAPI is that DPAPI supports two types of "stores": machine stores and userstores. User stores are more secure because they prevent user A from decryptingsecrets encrypted by user B. Machine stores are less secure, because anyone whocan log on to the machine can decrypt machine-store secrets. The samplespresented here use machine stores. You can increase the security of the machinestore by passing optional entropy values to DPAPI functions. The entropy valuesbecome part of the symmetric encryption keys that DPAPI uses. The downside tosupplying entropy values is that they constitute yet another set of keys thatyou must somehow store securely. As you can see, security is a rat hole thatgoes infinitely deep.

A side effect of using DPAPI andmachine stores is that connection strings will encrypt differently on everymachine. In other words, you must encrypt your connection strings on the Webserver where they will be decrypted. You can't run Encrypt.exe on one PC andapplications that use the encrypted connection strings on another and expectthem to work.

An additional resource to look towhen considering how to store database passwords and other configuration datasecurely is Microsoft's PAG Configuration Management Application Block for .NET(http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/cmab.asp).This application block - one of several Microsoft has published to promote bestprogramming practices - lets you store configuration data in a variety ofdifferent mediums. It also features built-in support for encryption of content.Not coincidentally, its architecture closely parallels what's coming withASP.NET 2.0. Stay tuned!

The samplecode in this article is available for download.

 

Jeff Prosise is theauthor of several books, including Programming Microsoft .NET (Microsoft Press, 2002). He's also acofounder of Wintellect (http://www.wintellect.com),a software consulting and education firm that specializes in .NET. Have aquestion for this column? Submit queries to [email protected].

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like