Skip navigation

Passwords in Web.config?

Use DPAPI to Secure Database Passwords

Q. Is it safe to put passwords and other secrets 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 HTTP handler named HttpForbiddenHandler, which fails requests and informs the user that "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, is vulnerable to file disclosure hacks resulting from a bug in an ISAPI DLL named ISM.dll. Putting plaintext - that is, unencrypted - "secrets" in a Web.config file on that server would be risky, at best.

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

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

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

 

Using DPAPI

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

One problem DPAPI poses for ASP.NET programmers is that because it isn't wrapped in the .NET Framework Class Library, you must use P/Invoke - that is, callouts to unmanaged code - to call it. Fortunately, Microsoft has already done most of the work for you. The MSDN document "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 one technique for encrypting database connection strings and storing them securely in Web.config using Microsoft's DataProtector class. The first contains the source code for a command-line utility named Encrypt.exe that you can use to encrypt connection strings - or any other data, for that matter (see Figure 1). The command:

encrypt server=

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

encrypts the indicated connection string and displays the result as a rather long base-64-encoded string. Observe that 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 connection strings and other secrets.

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



  

    

  

Figure 2: This Web.config file contains an encrypted connection string (the lines wrap for formatting purposes).

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



  

    

  



 

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

Before you can run these samples, you must build an assembly containing the DataProtector class. Get the source code from the URL I mentioned earlier and write it to a file named DataProtect.cs. Then execute the following command in a Visual Studio.NET command 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 host application. To use it with Encrypt.exe (which dynamically links to DataProtect.dll), put a copy of DataProtect.dll in the same directory as Encrypt.exe, or, if you prefer, strong-name DataProtect.dll and install it in the Global Assembly Cache. A compiled version of Encrypt.exe is included with the files that accompany this article (see end of article for download details).

Does using DPAPI ensure that secrets are 100 percent secure? Of course not. But it raises the bar for attackers, and it relies on proven cryptographic devices such as Triple-DES encryption and PKCS5 key derivation. (For an excellent technical overview of DPAPI, 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 far safer than storing secrets in plaintext. And it strikes a reasonable balance between level of security and ease of implementation.

 

Using the Registry as Well

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

To demonstrate, the REG file shown in Figure 4 creates a new key named HKEY_LOCAL_MACHINE/SOFTWARE/MyWebApp in the registry 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_MACHINE\SOFTWARE\MyWebApp]

"ConnectionString"="AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAUsAE5

G7asEGKnwESpz+6cQQAAAACAAAAAAADZgAAqAAAABAAAAATEuHfkynIFA

vWubbBboorAAAAAASAAACgAAAAEAAAAFJtFcgwkM4qDpkb5SQ/Mb9AAAA

AyBSDtonw8GOCGmGpcWLYMYlVfXt1lTimcTYsv5+L0bJZoNDNLoJzTVKx

+eoDTePZcik5Cj6+jOa1JG9rJ3R+TxQAAACxHb3MHN9ducLZ1hpX/S57Y

VVK/A=="

Figure 4: Run this registry script - Secret.reg - to place an encrypted connection string in the registry under HKEY_LOCAL_MACHINE/SOFTWARE/MyWebApp (once more, lines wrap for formatting purposes).


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

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


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

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

As an experiment, temporarily remove the ASP.NET account from the registry key's access control list and refresh the page. You should see a security exception, because even ASP.NET can't read the key. (If you don't see a security exception, that's trouble. You might 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 the registry, decrypts it, and displays it.

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

 

Conclusion

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

A side effect of using DPAPI and machine stores is that connection strings will encrypt differently on every machine. In other words, you must encrypt your connection strings on the Web server where they will be decrypted. You can't run Encrypt.exe on one PC and applications that use the encrypted connection strings on another and expect them to work.

An additional resource to look to when considering how to store database passwords and other configuration data securely 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 best programming practices - lets you store configuration data in a variety of different mediums. It also features built-in support for encryption of content. Not coincidentally, its architecture closely parallels what's coming with ASP.NET 2.0. Stay tuned!

The sample code in this article is available for download.

 

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

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish