Por regla general, en un fichero App.Config, podemos encontrar dos secciones predefinidas, que son las que se usan habitualmente en los programas. Me refiero a la sección connectionStrings, y a la sección appSettings.
En la sección connectionStrings, se guardan cadenas de conexión a bases de datos, mientras que en appSettings se guardan pares clave-valor que posteriormente podemos recuperar desde la aplicación. Un ejemplo de fichero de configuración puede ser el siguiente:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="DB2" connectionString="Provider=IBMDADB2;Data Source=DSNRNP;" />
<add name="SQL" connectionString="Data Source=CARGASIP;Initial Catalog=Mercury;Persist
Security Info=True;User ID=us123;Password=1234" />
</connectionStrings>
<appSettings>
<add key="Licencia" value="123456"/>
<add key="rutaImagenes" value="c:\imagenes"/>
</appSettings>
</configuration>
Si queremos podemos hacer una encriptación de las secciones:
Configuration cm = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSection cs = cm.GetSection("connectionStrings");
if (!cs.SectionInformation.IsProtected)cs.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
cs.SectionInformation.ForceSave = true;
cm.Save(ConfigurationSaveMode.Full);
}
Por defecto, .Net incluye dos algoritmos de encriptación:DataProtectionConfigurationProvider y RsaProtectedConfigurationProvider. El primero realiza una encriptación usando la librería DPAPI (Data Protection API de Windows). El segundo usa una pareja de claves pública/privada para encriptar por RSA, y es el que se usará si no se indica ningún algoritmo de encriptación. Ambos métodos son bastante seguros, aunque el DPAPI añade la seguridad de que la clave es específica del ordenador en el que se ejecuta la aplicación (con las ventajas e inconvenientes que ello conlleva).
Para encriptar la sección appSettings, realizaríamos un proceso análogo:
ConfigurationSection apps = cm.GetSection("appSettings");
if (!apps.SectionInformation.IsProtected)
{
apps.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
apps.SectionInformation.ForceSave = true;
cm.Save(ConfigurationSaveMode.Full);
}
Hemos visto que no resultaría complicado encriptar todo el appSettings para que no se pudiera acceder al valor de la licencia. ¿Pero que ocurre con el valor de rutaImagenes?. También se encriptaría, y poder cambiar el valor de esa clave simplemente abriendo el fichero de configuración con un editor de texto sería imposible, lo que nos obligaría a compilar otra vez la aplicación o programar algo que nos permitiera modificar el valor desde el programa. Vemos ahora que sería muy útil poder encriptar solo ciertos valores del appSettings y otros no. Ahora bien, lo que hemos visto hasta ahora, cifraba todo el appSettings. ¿Cómo podríamos resolver este problema?
Una posible solución pasa por crear una
sección personalizada, similar al appSettings,
y guardar ahí los valores sensibles que queramos encriptar, dejando la sección appSettings sin encriptar, y
perfectamente modificable (aunque, por supuesto, se podría hacer al revés).
Por ejemplo,
<configSections>
<section name="mySettings" type="System.Configuration.NameValueSectionHandler" />
</configSections>
<mySettings>
<add key="Licencia2" value="123456"/>
</mySettings>
Y ahora encriptamos:
Configuration cm = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSection ms = cm.GetSection("mySettings");
if (!ms.SectionInformation.IsProtected)
{
ms.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
ms.SectionInformation.ForceSave = true;
cm.Save(ConfigurationSaveMode.Full);
}
0. ¿Alguna otra solución elegante para encriptar ciertos valores del appsettings?
1. ¿En qué momento sería mejor (best way) hacer la encriptación por código de la sección personalizada ? Si se hace al inicio de la aplicación (sea Windows Forms o ASP.NET) es algo que no me convence.
Se podría hacer en la máquina de desarrollo, antes de desplegar o instalar la aplicación en una máquina de producción, lo cual puede ser bastante incómodo, además de tener que "acordarse" de la encriptación. Eso se solucionaría con un script automatizado (tipo msbuild) para de alguna forma realizar la encriptación.
2. Si la encriptación usa DPAPI, se realiza basándose en la máquina donde se encripta, por tanto, la encriptación se debería hacer en el momento del despliegue o la instalación, y no en la primera ejecución (en evento Page_load o Form_Load). Esto enlaza con la idea de scripts automatizados de despliegue(deploy).
Si se utiliza MsBuild o similares para automatizar el deploy, necesitaríamos algún Tasks para poder realizarlo, o incluso un Exec Command a alguna utilidad que nos permita hacerlo (me pregunto si con aspnet_regiis ???).
Si fuese un Installer (imaginamos que teniendo un setup.msi), podría ser algo así como vi en foros de MSDN, pasando la información necesaria al contexto del Installer (Custom Action):
Configuration config =ConfigurationManager.OpenExeConfiguration(<executable path>);
if (config == null) return;
ConfigurationSection section = config.GetSection(<section name>);
if (section == null) return;
// Make sure that the section is not yet protectedif (section.SectionInformation.IsProtected) return;
if (section.SectionInformation.IsLocked) return;
//Protecting the specified section with the specified providersection.SectionInformation.ProtectSection("RsaProtectedConfigurationProvider");
// Force saving of the sectionsection.SectionInformation.ForceSave = true;
config.Save(ConfigurationSaveMode.Modified);
Referencias:
http://msdn2.microsoft.com/en-us/library/89211k9b(VS.80).aspx
http://msdn2.microsoft.com/en-us/library/system.configuration.sectioninformation.protectsection.aspx
DPAPIProtectedConfigurationProvider.
RSAProtectedConfigurationProvider uses the RSACryptoServiceProvider to encrypt configuration sections
using RSA public key encryption to encrypt and decrypt data.
DPAPIProtectedConfigurationProvider uses the Windows Data Protection API (DPAPI) to encrypt configuration sections using the built-incryptography capabilities of Windows. You can also create your own protected settings providers if needed. While a user will have a hard time dealing with encrypted data, ASP.NET has no problems. You can use both of these providers in ASP.NET code.