martes, 1 de julio de 2014

El Demo del Día: Proteger el Archivo de Configuración

Proteger el Archivo de Configuración

Problema de Seguridad

En .NET, el archivo de configuración de las aplicaciones (App.config o Web.config) se divide en secciones como connectionStrings para guardar cadenas de conexion o appSettings para guardar variables de aplicación, y muchas veces necesitamos proteger la información de una o ambas secciones para que no sea vista fácilmente, por ejemplo el usuario y clave de la cadena de conexión.

Solución al Problema de Seguridad

Podemos implementar un código que permita cifrar la cadena de conexión y luego descifrarla, pero sería muy trabajoso y no tan eficiente. Desde .NET Framework 2.0 podemos proteger una sección del archivo de configuración usando el método "ProtectSection" de la clase "SectionInformation", el cual marca la sección para que se cifre, de modo que se escriba en el disco en formato cifrado.

Sintaxis: ConfigurationElement.SectionInformation.ProtectSection(ProveedorProtección)

Los proveedores de protección incluidos son los siguientes: DPAPIProtectedConfigurationProvider y RSAProtectedConfigurationProvider.

Nota: Por defecto, sino se coloca ningún parámetro con el Proveedor de Protección (nulo o vacío), se
usa por defecto "RSAProtectedConfigurationProvider", pero en .NET Framework 2 y 3.5 puede generar un error, mejor es especificar el proveedor.

Más informaciónSectionInformation.ProtectSection (Método)

Crear una Aplicación Windows Forms de Prueba

Creamos una aplicación WinForms en C# llamada "Seguridad_AppConfig" y llamamos al formulario "frmPrueba" sobre el cual arrastramos 2 controles Label y 2 Textbox, similar a la siguiente figura:


Escribimos el siguiente código para el formulario:
using System;
using System.Drawing;
using System.Windows.Forms; //Form
using System.Data.SqlClient; //SqlConnection
using System.Configuration; //ConfigurationManager

namespace Seguridad_AppConfig
{
    public partial class frmPrueba : Form
    {
        public frmPrueba()
        {
            InitializeComponent();
        }

        private void cargarDatos(object sender, EventArgs e)
        {
            using (SqlConnection con = new SqlConnection
                (ConfigurationManager.ConnectionStrings["conNW"].ConnectionString))
            {
                con.Open();
                txtCadenaConexion.Text = con.ConnectionString;
                txtRutaLog.Text = ConfigurationManager.AppSettings["RutaLog"];
            }
        }
    }
}

Nota: La función controladora "cargarDatos" esta asociado al evento "load" del "formulario".

Archivo de Configuración

Definimos las siguiente secciones en el archivo de configuración (App.Config):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="conNW" providerName="System.Data.SqlClient" connectionString="uid=UsuarioNW;
      pwd=123456;data source=servidor;initial catalog=Northwind"/>
  </connectionStrings>
  <appSettings>
    <add key="RutaLog" value="C:\Logs\"/>
  </appSettings>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
</configuration>

Clase con Métodos para Proteger Secciones del Archivo de Configuración

Creamos una clase llamada: "ProtegerConfig" con 3 métodos estáticos:
1. ProtegerConexion: Cifra solo la sección "connectionStrings".
2. ProtegerVariables: Cifra solo la sección "appSettings".
3. ProtegerConexionVariables: Cifra las 2 secciones, "connectionStrings" y "appSettings".

using System;
using System.Configuration;
namespace Seguridad_AppConfig
{
    public class ProtegerConfig
    {
        public static void ProtegerConexion()
        {
            Configuration configura = ConfigurationManager.OpenExeConfiguration
                (ConfigurationUserLevel.None);
            if (!configura.ConnectionStrings.SectionInformation.IsProtected)
            {
                configura.ConnectionStrings.SectionInformation.ProtectSection
                    ("RSAProtectedConfigurationProvider");
                configura.Save(ConfigurationSaveMode.Full, true);
            }
        }

        public static void ProtegerVariables()
        {
            Configuration configura = ConfigurationManager.OpenExeConfiguration
                (ConfigurationUserLevel.None);
            if (!configura.AppSettings.SectionInformation.IsProtected)
            {
                configura.AppSettings.SectionInformation.ProtectSection
                    ("RSAProtectedConfigurationProvider");
                configura.Save(ConfigurationSaveMode.Full, true);
            }
        }

        public static void ProtegerConexionVariables()
        {
            Configuration configura = ConfigurationManager.OpenExeConfiguration
                (ConfigurationUserLevel.None);
            bool grabar = false;
            if (!configura.ConnectionStrings.SectionInformation.IsProtected)
            {
                configura.ConnectionStrings.SectionInformation.ProtectSection
                    ("RSAProtectedConfigurationProvider");
                grabar = true;
            }
            if (!configura.AppSettings.SectionInformation.IsProtected)
            {
                configura.AppSettings.SectionInformation.ProtectSection
                    ("RSAProtectedConfigurationProvider");
                grabar = true;
            }
            if(grabar) configura.Save(ConfigurationSaveMode.Full, true);
        }
    }
}

Nota: Antes de proteger una sección primero averiguamos si ya fue protegida, ya que si grabamos una sección que ya fue protegida generará una excepción.

Modificar la llamada en el método Main de la clase "Program"

Aumentar en el método Main() de la clase Program.cs la llamada a los 3 método estáticos:

using System;
using System.Windows.Forms;

namespace Seguridad_AppConfig
{
    static class Program
    {
        /// <summary>
        /// Punto de entrada principal para la aplicación.
        /// </summary>
        [STAThread]
        static void Main()
        {
            //ProtegerConfig.ProtegerConexion();
            //ProtegerConfig.ProtegerVariables();
            //ProtegerConfig.ProtegerConexionVariables();
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new frmPrueba());
        }
    }
}

Nota: Están comentados para probar uno por uno y ver los resultados.

Probar la aplicación que cifra el archivo de configuración

Primero ejecutamos la aplicación con los métodos comentados y se mostrará la figura:


Ingresar al directorio donde se crea el ejecutable de la aplicación: "Seguridad_AppConfig\bin\Debug\" y abrir con el bloc de notas el archivo de configuración: "Seguridad_AppConfig.exe.config" observar que esta igual como se definió al inicio (su tamaño es 1kb).

Parar la ejecución, descomentar la línea ProtegerConfig.ProtegerConexion(); del método Main(), grabar y compilar nuevamente. Ejecutar el exe desde fuera del Visual Studio, es decir el archivo: "Seguridad_AppConfig.exe" de la carpeta bin y observar el archivo de configuración, que ha cambiado (su tamaño es ahora 58 kb) y se ha cifrado la sección "connectionStrings".

Parar la ejecución, descomentar la línea ProtegerConfig.ProtegerVariables(); del método Main() y comentar la anterior, grabar y compilar nuevamente. Ejecutar el exe desde fuera del Visual Studio, es decir el archivo: "Seguridad_AppConfig.exe" de la carpeta bin y observar el archivo de configuración, que ha cambiado (su tamaño es ahora 58 kb) y se ha cifrado la sección "appSettings".

Finalmente, parar la ejecución, descomentar la línea ProtegerConfig.ProtegerConexionVariables(); del método Main() y comentar la anterior, grabar y compilar nuevamente. Ejecutar el exe desde fuera del Visual Studio, es decir el archivo: "Seguridad_AppConfig.exe" de la carpeta bin y observar el archivo de configuración, que ha cambiado (su tamaño es ahora 59 kb) y se ha cifrado ambas secciones "connectionStrings" y "appSettings".

Comentario Final

Aunque el demo es sobre WinForms, se puede usar el mismo código en todos los tipos de aplicaciones (ASP .NET WebForms, WPF, WCF, Workflow, etc) para cifrar el archivo de configuración usando un algoritmo asimétrico como RSA. Espero les sirva.

Descarga:
Demo08_Seguridad_AppConfig

2 comentarios:

  1. Esta muy bueno, sino leo el blog nunca me iba a anterar de este tipo de seguridad, pero para que otros no tengan problemas al probar su demo es que tienen que agregar el archivo de manifiesto configuarlo como administrador

    por que la mayoria de las maquinas esta activado la seguridad de windows.

    ResponderBorrar