BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Securing web.config with Encryption Certificates on Windows and Azure

Securing web.config with Encryption Certificates on Windows and Azure

Bookmarks

Key Takeaways

  • Encryption certificates can reduce the number of people who have access to secrets.
  • Encryption certificates alone do not lessen the need for a robust, multi-layered security plan.
  • The pfx decryption key file must be stored in a safe, offline location separate from its password.
  • Knowledge of PowerShell will make certificate related tasks easier.

With data breaches becoming ever more common, it is vital applications are made as secure as possible. One major area where security is often lax is the web.config file. Usually stored in plain text, an intruder who gains access to this file can then easily access databases and other resources both internal and external.

It wasn’t always this way. When .NET was first created, most applications were running within a single Windows domain. Usually the web.config file didn’t need to store passwords; database access rights were directly granted to the user account the application was running under.

With modern applications, this is no longer an option. In this modern world of distributed systems, databases and other resources are often running outside of the Windows domain and may not even be controlled by the same company. In this climate, the right storage of passwords, application tokens, and other system-level identifiers is essential.

The technique presented here isn’t new; Jouni Heikniemi talked about it in 2013 as did the SQL Azure team three years before that. But over the years, specific steps no longer work due to deprecated tools and changing user interfaces. Still, the original concept is still valid.

Incremental Security

Before we talk about specific techniques, it is important to understand encrypting a web.config file is only one step in the overall security plan for an application. It is important to understand what protections it does and does not offer.

For the web.config to be useful, the application has to be able to decrypt it. If you were to store the decryption key in a file next to web.config, obviously an attacker would just grab that file in addition to the web.config file. Embedding the decryption key in the application adds an extra step, but that too can easily be overcome simply by running a decompiler against the stolen code.

Decryption certificates are different. They are stored at the OS level so attackers can’t simply grab them from the hard drive. Unless you explicitly allow it, they cannot even be exported from the computer they reside on.

That’s not to say this is a perfect solution. If the attacker can write to the application’s directory, the application can be changed to decrypt and reveal the contents of web.config.  This means it is still vital to write-protect the directories containing application code. Ideally only your build server can write to your application directories on your production server, but that is outside the scope of this article.

Another concern is protecting copies of the decryption certificate itself. They will be needed for setting up new servers, but otherwise should be kept off-line in a safe or other secure location. Encryption certificates, on the other hand, can be shared as needed.

Creating the Encryption Certificate

Encryption certificates consist of a public and a private key. They are similar to SSL/TLS certificates, but are self-signed since they don’t need to prove who created the encrypted file. In the past Windows developers would use makecert.exe, but that tool has been deprecated. (It can still be found in the Windows SDK.)

These days PowerShell is considered to be the correct tool for creating certificates on Windows machines. To create a certificate, use these commands. 

WARNING: You may need to run the PowerShell commands as an administrator.

$cert = New-SelfSignedCertificate -Type DocumentEncryptionCert -Subject "CN=DevConfig" -KeyExportPolicy Exportable -KeySpec KeyExchange

Export-Certificate -Cert $cert -FilePath ".\DevConfig.cer"

$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

Export-PfxCertificate -Cert $cert -FilePath ".\DevConfig.pfx" -Password $mypwd

$cert

As the name implies, the New-SelfSignedCertificate command creates the certificate itself. In this example it is named “DevConfig” but you can call it anything you want.

Next, we export the encryption certificate into a “.cer” file using the Export-Certificate command. This is the public key used to create encrypted configuration files.

The next two lines allow us to export the decryption certificate as a “.pfx” file using Export-PfxCertificate. It is vital this file is stored in a secure, offline location separate from the password you defined using ConvertTo-SecureString. (And obviously you should replace “1234” with a more secure password.)

You’ll need to know the thumbprint of the certificate for later steps. The last line, “$cert” by itself, will display the thumbprint on the screen. The thumbprint is not considered to be a secret.

Importing the Encryption Certificate into Windows

If you are setting up a machine that can encrypt, but not decrypt, configuration files then use this step. Otherwise skip to the next heading.

Import-Certificate -Filepath ".\DevConfig.cer" -CertStoreLocation cert:\LocalMachine\My

See Import-Certificate for more information.

Importing the Decryption Certificate into Windows

This step will actually import both the decryption and encryption certificate (a.k.a. the public and private key). 

$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

Import-PfxCertificate -FilePath ".\DevConfig.pfx" -CertStoreLocation Cert:\LocalMachine\My -Password $mypwd

If you import a certificate with Import-PfxCertificate, it defaults to being non-exportable. Meaning, someone cannot then export from that machine to another. While this is desirable for production scenarios, you may want to mark it as exportable for development machines.

You can use the Certificate Management Tool to verify the certificate was properly installed.

Importing the Decryption Certificate into Windows Azure

Certificate decryption is not available in the free version of Azure App Services. As of this article, you need at least the B1 tier, which allows for SSL connections.

In the Azure Portal, use the “SSL Settings” tab. Then press the “Upload Certificate” button to install the certificate. You should then see it at the bottom of the screen.

(Click on the image to enlarge it)

Next you need to expose the certificates to the application. This is done by adding the WEBSITE_LOAD_CERTIFICATES key to the Application Settings tab. You can use multiple comma-separated thumbprint values or can set this value to “*” to expose all your certificates to your web application.

(Click on the image to enlarge it)

The Protected Configuration Provider

Encryption and decryption is handled by a ProtectedConfigurationProvider. The one used in this article is called Pkcs12ProtectedConfigurationProvider. It was originally created by Microsoft, but minor changes have been made for compatibility with Azure App Services.

A ProtectedConfigurationProvider inserts itself into the configuration reader pipeline, so your application code doesn’t really have to be aware of it. This is useful when you want the flexibility of using both encrypted and non-encrypted configuration files. 

You can add Pkcs12ProtectedConfigurationProvider directly to your project or download via the WebConfigEncrypter NuGet package. This article assumes you will be using the NuGet package.

Preparing web.config for Encryption

Once the Pkcs12ProtectedConfigurationProvider class is added to your project, you’ll need to prepare the web.config file for encryption.  

1. Add this section to your web.config file.

<configuration>
  [...]
  <configProtectedData>
    <providers>
      <add name="Pkcs12Provider" thumbprint="1234123412341234123412341234123412341234" type="WebConfigEncrypter.Pkcs12ProtectedConfigurationProvider, WebConfigEncrypter" storeLocation="LocalMachine"/>
    </providers>
  </configProtectedData>

2. Change the thumbprint attribute to match your certificate.

3. If you are not using the NuGet package, update the type attribute to match the fully quantified class and assembly name of the DLL your ProtectedConfigurationProvider.

Encrypting connection strings in web.config

Before you begin, ensure you have a backup copy of your web.config file.

From the Visual Studio command line, type “where aspnet_regiis”. Then copy the following files into this folder. This will allow the aspnet command line tools to use your ProtectedConfigurationProvider class.

  • WebConfigEncrypter.dll 
  • System.Configuration.ConfigurationManager.dll
  • System.Security.Cryptography.Xml.dll

Then run this command from the same folder as your web.config file.

aspnet_regiis -pef "connectionStrings" "." -prov "Pkcs12Provider"

Your encrypted configuration section should now look something like this:

<connectionStrings configProtectionProvider="Pkcs12Provider">
    <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
      xmlns="http://www.w3.org/2001/04/xmlenc#">
      <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes192-cbc" />
      <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
        <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
          <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
          <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <KeyName>rsaKey</KeyName>
          </KeyInfo>
          <CipherData>
            <CipherValue>Moy/a2XO2zvnn/HZW53DyC8aAJWo16+0KmnpC4SCSmuQZU0RT+HNFEA33pAGCzve+m6MTaRzhx6jVVRoAvpSNzfYG1bU1z7a1YnbW4OGxrmYYfdWW6cZQZ57dZnL6YSAlkJ5WlqPDGUPJa6FV/hTic3x4fJYy5vdSucmO6X3opuo1998LWNkL6fIS4WkjkG/SOFbI2Qx3HHogdN670jDHKNDON1z7bFHhLNyVj7RTO3xuQN9kF4PqbFtvwm1bYXTbZpdNxu/fcXZKONSAu8HN3QX5vTRyP/I4BG+NK7TUig3gxD4tq9GR7aSSGKJyt02PiCEO0JpyyIbHZ9xbck9kw==</CipherValue>
          </CipherData>
        </EncryptedKey>
      </KeyInfo>
      <CipherData>
        <CipherValue>TeV0yJaFlEhpyZUlQoG7M3O7sfQ7uG3ndgmhxipOrwoEsrI+Zvt1NI7arefOFWGNW4CEaoLo4mKy2Kwr4ZgK+6rAwOmx1IRyheWtF7z/8+CiGOqSRXLyGEkDQBEVOWKU0Y6TaWtPu0ZM3bp5pvKaztBnthgGnrGYmigaufu5rZW1GWPtHyL2iWdAkU9iaf+AOpA/GSvoVtZmnfJ1rwy6U8PTO0h0Ws/PdkcOKuXGkx31t/Y32ivFoy7xYPnPt/Z/aNMiHvbO7faQAwuJ/NsG9G1FFRRHCqc73TUsRdKHVuf17BEp526RG6RBZtM3F3V3o0d8/sLmyrNI9tFfksB4qcWiN4P+BRtGr0iacmBfBOvAFSozfUYxjMpx+BYPOpD1pf4fMFoKxxKeJYY31XqZoQLp75RgmWhWYm8URHq4Cjs=</CipherValue>
      </CipherData>
    </EncryptedData>
  </connectionStrings>

That’s it for Windows IIS. The configProtectionProvider key will tell your application which decryption class and certificate to use. If it doesn’t work, rerun the Import-PfxCertificate command described above.

Encrypting Custom Configuration Sections

In addition to connection strings, you can encrypt custom configuration sections built using IConfigurationSectionHandler. To do this, you must copy the library that defines the custom configuration class into the same folder as aspnet_regiis just like you did with WebConfigEncrypter.dll.

In your configSections list, make sure you use the fully quantified class and assembly name for the custom configuration class. For example,

  <configSections>
    <section name="protectedSettings" type="MyConfigSectionHandler.MyHandler, WebApplication1" />
  </configSections>

This is required for aspnet_regiis even when you can otherwise just provide the class name. 

You can then run the encryption command again, replacing the -pef parameter with the section name.

aspnet_regiis -pef "protectedSettings" "." -prov "Pkcs12Provider"

Special Considerations for Azure App Services

Because Azure App Services store and expose certificates under the current user rather than the machine, you will need to change the storeLocation attribute as shown below.

   <add name=“Pkcs12Provider” thumbprint=“1234123412341234123412341234123412341234" type=“WebConfigEncrypter.Pkcs12ProtectedConfigurationProvider, WebConfigEncrypter” storeLocation=“CurrentUser”/>

About the Author

Jonathan Allen got his start working on MIS projects for a health clinic in the late 90's, bringing them up from Access and Excel to an enterprise solution by degrees. After spending five years writing automated trading systems for the financial sector, he became a consultant on a variety of projects including the UI for a robotic warehouse, the middle tier for cancer research software, and the big data needs of a major real estate insurance company. In his free time he enjoys studying and writing about martial arts from the 16th century.

Rate this Article

Adoption
Style

BT