Encrypt sections of Web.Config or App.Config

March 3, 2008 18:03 by garrymc

In many application be they web or otherwise it can be important to secure parts of the configuration files incase the file is compromised by a hacker or you simply don't want anyone to know what the true values are. It turns out that this is fairly easy to do using the ASP.NET IIS Registration Tool (Aspnet_regiis.exe) (obvious right?). To illustrate the point I'll show you a typical config file and what's required to encrypt parts of it.

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <!--
   3:   Initial Config file without taking encryption into concideration
   4: -->
   5:  
   6: <configuration>
   7:   <configSections>
   8:     <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.
   9:             Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data,
  10:             Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  11:   </configSections>
  12:   <dataConfiguration defaultDatabase="MyDatabase">
  13:     <providerMappings>
  14:       <add databaseType="Microsoft.Practices.EnterpriseLibrary.Data.Sql.SqlDatabase, 
  15:             Microsoft.Practices.EnterpriseLibrary.Data, Version=3.0.0.0, Culture=neutral,
  16:             PublicKeyToken=b03f5f7f11d50a3a" name="System.Data.SqlClient" />
  17:     </providerMappings>
  18:   </dataConfiguration>
  19:  
  20:   <appSettings>
  21:     <add key="BufferSize" value="999999"/>
  22:     <add key="SomeService.UserName" value="secure-username"/>
  23:     <add key="SomeService.Password" value="secure-password"/>
  24:   </appSettings>
  25:   
  26:   <connectionStrings>
  27:     <add name="MyConnectionString" connectionString="Data Source=.;
  28:         Integrated Security=SSPI;Initial Catalog=MyApplicationDB;" 
  29:         providerName="System.Data.SqlClient" />
  30:   </connectionStrings>
  31: </configuration>

The settings of each of these can easily be retrieved using the following code which is included in the download as a console app.

   1: static void Main(string[] args)
   2: {
   3:     // Write out the contents of the config file.
   4:     Console.WriteLine("MyConnectionString = " + 
   5:         ConfigurationManager.ConnectionStrings["MyConnectionString"]);
   6:     Console.WriteLine("BufferSize = " + ConfigurationManager.AppSettings["BufferSize"]);
   7:     Console.WriteLine("SomeService.UserName = " + 
   8:         ConfigurationManager.AppSettings["SomeService.UserName"]);
   9:     Console.WriteLine("SomeService.Password = " + 
  10:         ConfigurationManager.AppSettings["SomeService.Password"]);
  11:  
  12:     Console.ReadLine();
  13: }

In the example above you may want to encrypt the connection string and two of the keys in appSettings, but probably not the BufferSize property as you'd like that to be easily accessible or at least readable on the production server. The problem we have is that the Aspnet_regiis.exe tool only works on entire sections ie appSettings, connectionStrings. So encrypting the connectionStrings section is fairly straightforward and can be done with the following command lines:

   1: copy App.Config App.Config.original
   2: rename App.config web.config
   3: aspnet_regiis -pef connectionStrings . -prov DataProtectionConfigurationProvider
   4: rename web.config App.config

So this is a little strange and the reason is that the tool only works with web.config files, however by renaming, then processing our app.config file we can get the desired result. I've included this as a batch file in the project download. You just need to run the batch file in the directory that your app.config file resides in. If you want to run this against your web.config then you only need the third line. The new config file should look something like this (I've reformatted it a bit):

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <!--
   3:   Initial Config file without taking encryption into concideration
   4: -->
   5:  
   6: <configuration>
   7:   <configSections>
   8:     <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.
   9:         Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data,
  10:         Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  11:   </configSections>
  12:   <dataConfiguration defaultDatabase="MyDatabase">
  13:     <providerMappings>
  14:       <add databaseType="Microsoft.Practices.EnterpriseLibrary.Data.Sql.SqlDatabase,
  15:          Microsoft.Practices.EnterpriseLibrary.Data, Version=3.0.0.0, Culture=neutral,
  16:          PublicKeyToken=b03f5f7f11d50a3a" name="System.Data.SqlClient" />
  17:     </providerMappings>
  18:   </dataConfiguration>
  19:  
  20:   <appSettings>
  21:     <add key="BufferSize" value="999999"/>
  22:     <add key="SomeService.UserName" value="secure-username"/>
  23:     <add key="SomeService.Password" value="secure-password"/>
  24:   </appSettings>
  25:   
  26:   <connectionStrings configProtectionProvider="DataProtectionConfigurationProvider">
  27:     <EncryptedData>
  28:       <CipherData>
  29:         <CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAALNO8kQcyL0SKbOQ8HQGp0AQAAAACAAA
  30:         AAAADZgAAqAAAABAAAAAd1TPFE24MHo78J8HYFRXKAAAAAASAAACgAAAAEAAAAAJ8yjPvsnqP0Lo
  31:         Qt05ynrWoAQAANJdt/En21VeyYb8OJcTE3p+MeKOMG4xsFPzjlrHnyaJmVEekYVMCkX2RG/1pV1J
  32:         DfsaCAXKSyBnxDiCM5yAQEA5TSxeedQYvKXWBWrUxMMIjNUmjZDsbc8I/eBKarMCHav7d+oguijn
  33:         HFf948x48Z25434QY1lZ4hnkP7oW5dO9639iaT3+uWJRoARzHzcfvNYxtJ/+jowoZyt+JRcHjR1Q
  34:         MBD/CMsehNh4nQeWX2zp09tYm2EdIj7LmSyWwrE9Livf1qs/IBGRv1zWPIue956mAwDbXPg4tuF1
  35:         Ml4Qdm3rj9+mCp/sht80ixkWM9nID8/OaqZqzaaK/J/3vMjZF+w4SeFws8Vo40xb48/Mr1Q5EOZe
  36:         JMNa1ySiard4pckR1Xd3rRPA+J1+6BBTCws0qsmBtd5pVs0KW0xt0HIlUELA7NYIsXML+zGFgJ2C
  37:         TTGcTQsUF5y3+soUeXxfM2Eq3NqSVouWJMImT2udvR6PCiHGsNMiT9/ZK8TL0C5aAzD47IAc0ngA
  38:         DF/uTcX8ERWUy7LPwIlDRXamTlRmVvXt712na83nFf+FExhQAAADDz+jCgtqU0npcs6WJgVP/+QU
  39:         cQQ==</CipherValue>
  40:       </CipherData>
  41:     </EncryptedData>
  42:   </connectionStrings>
  43: </configuration>

So that's how easy it is to encrypt a section of the config file, however, we still have an issue with encrypting part of our appSettings. The problem is you can't selectively encrypt part of a section, its an all or nothing scenario. Therefore, to achieve our goal, we have to add a new section and encrypt that. This leaves us with a config file that looks like this:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <!--
   3:   Config file taking encryption into concideration
   4: -->
   5:  
   6: <configuration>
   7:   <configSections>
   8:     <section name="secureAppSettings" type="System.Configuration.NameValueSectionHandler,
   9:         System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  10:     <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.
  11:         Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, 
  12:         Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  13:   </configSections>
  14:   <dataConfiguration defaultDatabase="MyDatabase">
  15:     <providerMappings>
  16:       <add databaseType="Microsoft.Practices.EnterpriseLibrary.Data.Sql.SqlDatabase, 
  17:         Microsoft.Practices.EnterpriseLibrary.Data, Version=3.0.0.0, Culture=neutral, 
  18:         PublicKeyToken=b03f5f7f11d50a3a" name="System.Data.SqlClient" />
  19:     </providerMappings>
  20:   </dataConfiguration>
  21:  
  22:   <appSettings>
  23:     <add key="BufferSize" value="999999"/>
  24:   </appSettings>
  25:  
  26:   <secureAppSettings>
  27:     <add key="SomeService.UserName" value="secure-username"/>
  28:     <add key="SomeService.Password" value="secure-password"/>
  29:   </secureAppSettings>
  30:  
  31:   <connectionStrings>
  32:     <add name="MyConnectionString" connectionString="Data Source=.;Integrated Security=SSPI;
  33:         Initial Catalog=MyApplicationDB;" providerName="System.Data.SqlClient" />
  34:   </connectionStrings>
  35: </configuration>

The new parts have been highlighted and show that I've added a new secureAppSettings (can be anything) section and then added the section with the name value pairs that I want to encrypt. Now all I need do is modify the original batch file to include this section too:

   1: copy App.Config App.Config.original
   2: rename App.config web.config
   3: aspnet_regiis -pef connectionStrings . -prov DataProtectionConfigurationProvider
   4: aspnet_regiis -pef secureAppSettings . -prov DataProtectionConfigurationProvider
   5: rename web.config App.config

This then produces the following encrypted config file ready for deployment - well almost...

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <!--
   3:   Config file taking encryption into concideration
   4: -->
   5:  
   6: <configuration>
   7:   <configSections>
   8:     <section name="secureAppSettings" type="System.Configuration.NameValueSectionHandler,
   9:         System, Version=1.0.3300.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  10:     <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.
  11:         Configuration.DatabaseSettings,Microsoft.Practices.EnterpriseLibrary.Data, 
  12:         Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  13:   </configSections>
  14:   <dataConfiguration defaultDatabase="MyDatabase">
  15:     <providerMappings>
  16:       <add databaseType="Microsoft.Practices.EnterpriseLibrary.Data.Sql.SqlDatabase, 
  17:         Microsoft.Practices.EnterpriseLibrary.Data, Version=3.0.0.0, Culture=neutral,
  18:         PublicKeyToken=b03f5f7f11d50a3a" name="System.Data.SqlClient" />
  19:     </providerMappings>
  20:   </dataConfiguration>
  21:  
  22:   <appSettings>
  23:     <add key="BufferSize" value="999999"/>
  24:   </appSettings>
  25:  
  26:   <secureAppSettings configProtectionProvider="DataProtectionConfigurationProvider">
  27:     <EncryptedData>
  28:       <CipherData>
  29:         <CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAALNO8kQcyL0SKbOQ8HQGp0AQAAAACAAAA
  30:         AAADZgAAqAAAABAAAACF8idXnx0O07526CxQFh9BAAAAAASAAACgAAAAEAAAACNxLiQ6eAyzE8jZG
  31:         yUhwThYAQAAii89rtpXJsxeklxD/7LhjtLzzaRGxcCpWUDuI+uNKuN0GrDeKSXRB0s7tfumGdzkZM
  32:         iR6qlTE9ueIDWO8tm79pVJ3emkYSiBRGi4KXs5oAPugwyuZ/Qfq3LC2d1U07EWGvOBQK4SjTC56De
  33:         KKZDLwu+3WRBYLGLdJapCnMQ4lB/Ev29R9XYW9iqQfgOFE3iFdtcfts4+8Ig7y6CYe3A845VZWBD0
  34:         6SFu2obFZ8vGCRmF2qr0M9N9uxT+xOCRX7G5vkbH+TpFSiUQoV+cC5f9y98uCyj2xL60GxO+PZM8W
  35:         UJFYknRoX6SkVzfRwX+GWcdRc+nYSM1+I/hica32POuWur5lAhye0JO2SHTKSj66S+XjiM64fkh+b
  36:         HNp1LXf0ciEZQNIZeknVzClns6vHHDYYPI3iPYs+dTv3hoNPSDe4kee9BhuPWgRBS3uObPt6ySv46
  37:         QHJH4QWsUAAAAxcEHWBLJ1zw1Y5etR7t4EFeU5X8=</CipherValue>
  38:       </CipherData>
  39:     </EncryptedData>
  40:   </secureAppSettings>
  41:  
  42:   <connectionStrings configProtectionProvider="DataProtectionConfigurationProvider">
  43:     <EncryptedData>
  44:       <CipherData>
  45:         <CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAALNO8kQcyL0SKbOQ8HQGp0AQAAAACAAAA
  46:         AAADZgAAqAAAABAAAABv17Dh+HaRnrZppOfniJMaAAAAAASAAACgAAAAEAAAAF1mqOQdo+lvm45mQ
  47:         MwaiaKoAQAAHVHExOIDaItnnbRK7n/qHudiSUAq22t2qRTSxQDxE/lYyN2gA/cWHTw4njHVRu+MJN
  48:         awquuIFKW7RLFAlDYdJPA2xPHakwJXt6MLCOgdXmd+YojqP7AYHrPbFuRGF/8sNY+SlH0QOZya53D
  49:         AuljzMUQoE27BQbsCT+tLriMnGgmc8SaCjAFqqVc538zxxKLFnLZJLttHm5qAq7/y65abZDMjYo4Y
  50:         m4RpmhxQ5ZyITWpGt8RWabZ10MgWvcWS+QiL+ONVhs7lgR5wo0tDk1aoMh0f2VrSZPfXYoC8ZR2JV
  51:         0jqVSNasYmZN/+AO2G+6/S8YxETJqYD3BL8qFBtok9NUzvvoGMZYw+lJniSoLCBWtNoFpFM90/v0x
  52:         hCfRZZqiYj/8OOpJQT38ycG1N/zskhK/LAKe7loZGgPMl1wUjnFGpLOHW+f2ikVtYtHkH3+qxqny8
  53:         AeSZKovMnhfZlXUJx18G0Ugpvskmgju9aKtnE6UWLZbiiygJAfifQUjTwjFXgibQmuvFPxlWuUSW7
  54:         e6fBzVggx1zpDqnu5RGksN+zOg3S7IyiC79GihQAAAAWBM1NaI+3SdfalbKCGvuhxxTNSQ==
  55:         </CipherValue>
  56:       </CipherData>
  57:     </EncryptedData>
  58:   </connectionStrings>
  59: </configuration>

If we try and run our original console app the values for our secure settings won't be retrieved, which if you've been paying attention you'll no-doubt have worked this out as the values are no longer part of the appSettings section but part of the secureAppSettings section. So we need to update our original console app to look at this section instead:

   1: static void Main(string[] args)
   2: {
   3:     // Write out the contents of the config file.
   4:     Console.WriteLine("MyConnectionString = " + 
   5:         ConfigurationManager.ConnectionStrings["MyConnectionString"]);
   6:     Console.WriteLine("BufferSize = " + ConfigurationManager.AppSettings["BufferSize"]);
   7:  
   8:     // Access the secureAppSettings
   9:     NameValueCollection secureAppSettings = 
  10:         (NameValueCollection)ConfigurationManager.GetSection("secureAppSettings");
  11:     Console.WriteLine("SomeService.UserName = " + secureAppSettings["SomeService.UserName"]);
  12:     Console.WriteLine("SomeService.Password = " + secureAppSettings["SomeService.Password"]);
  13:  
  14:     Console.ReadLine();
  15: }

Running this version gives the same result as the original except that now all the parts that we want encrypted are. The really cool thing is that this works if the config is encrypted or not, which allows you to work with an un-encrypted version during dev and then have your Ops people encrypt it (once they put the real values in) just prior to deploy or as part of the deployment script.

kick it on DotNetKicks.com
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Currently rated 3.2 by 55 people

  • Currently 3.199999/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Comments