ASP.NET MVC: Encriptar la query string

Buenas! Este post surge debido a esta pregunta del foro de ASP.NET MVC. El usuario se pregunta si existe en el framework una manera built-in de encriptar la query string. Y la realidad es que no, no la hay, pero añadir una es muy sencillo y me da una excusa perfecta para poner un buen ejemplo del poder de los value providers.

ASP.NET MVC está construído de una forma bastante flexible, pero en el pipeline de una petición hay más o menos 4 pasos:

  1. Procesar la url para generar unos valores de ruta
  2. En base a dichos valores seleccionar una acción de un controlador
  3. Explorar la petición http en busca de parámetros (que pueden estar en la querystring, en formdata, etc)
  4. A partir de esos parámetros rellenar los parámetros de la acción del controlador

De todos esos pasos, el tercero es responsabilidad de los value providers: ellos inspeccionan la petición http en busca de parámetros y los dejan en “un sitio común”. Luego, en el paso cuatro, el model binder recoge los parámetros de este sitio común y a partir de ellos instancia los parámetros de la acción del controlador.

Ahora supongamos que nuestra querystring tiene algún parámetro encriptado. Para este post he supuesto que algunos parámetros de la querystring pueden ir encriptados, mientras que otros no (no se encripta toda la querystring, aunque la técnica seria la misma). Para el ejemplo supongo que los parámetros encriptados tienen un nombre que empieza por un subrayado (y no, el nombre en si mismo no se encripta).

Así, p.ej. voy a tener una querystring del tipo:

/accion/controlador?_foo=aIGf0UYsNARiBaGZr3blRg%3D%3D

Donde el valor del parámetro _foo está encriptado.

Ahora bien, a mi me gustaría evitar tener que colocar el código para desencriptar dichos valores en cada acción:

public ActionResult Test(string _foo)
{
    // Codigo a evitar:
    var realfoo = Crypto.DecryptValue(_foo);
    // ...
}

Aquí es donde entran los value providers. ASP.NET MVC trae de serie varios value providers que “inspeccionan” una petición http y miran los sitios más habituales donde se pueden encontrar datos:

image

Los value providers se registran en el sistema a través de una factoría, y esta captura muestra las factorías de value providers registradas por defecto en ASP.NET MVC4 RC. Cada factoría devuelve un tipo de value provider, es decir un value provider que mira en una parte de la petición http y por el nombre se puede más o menos deducir:

  1. ChildActionValueProviderFactory: Se usa cuando se invocan acciones hijas (a través de Html.Action o de Html.RenderAction). Es más de infraestructura de ASP.NET MVC que otra cosa.
  2. FormValueProviderFactory: Devuelve un value provider encargado de inspeccionar los parámetros de formdata (básicamente formularios enviados via POST).
  3. JsonValueProviderFactory: Introducido en MVC3 devuelve un value provider que inspecciona la petición http, buscando datos en el cuerpo de ésta que estén en formato json. Es el responsable de que en MVC3 puedas hacer un POST enviando datos en formato json y que el model binder los entienda.
  4. RouteDataValueProviderFactory: Devuelve un value provider que inspecciona los route values, o sea los parámetros establecidos a través de la tabla de rutas.
  5. QueryStringValueProviderFactory: Devuelve un value provider que inspecciona la querystring.
  6. HttpFileCollectionValueProviderFactory: Devuelve un value provider que inspecciona los datos de los ficheros subidos (usando <input type=”file”>).

Por supueso nosotros podemos crearnos nuestras propias factorías de value providers, y eso es justamente lo que haremos. En nuestro caso vamos a hacer un value provider que:

  1. Inspeccione la query string
  2. Seleccione aquellos parámetros que empiezan por un subrayado.
  3. Desencripte cada uno de esos parámetros y guarde en “este sitio común” dicho parámetro, pero con el valor desencriptado. Además el nombre del parámetro en el “sitio común” lo modificaremos quitándole el subrayado.

Veamos primero el código de la factoría:

public class QSEncriptedValueProviderFactory : ValueProviderFactory
{
    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        return new QSEncriptedValueProvider(controllerContext);
    }
}

Simple no? 🙂 Nos limitamos a devolver una instancia de nuestro value provider propio. Veamos su código:

 

public class QSEncriptedValueProvider : DictionaryValueProvider<string>
{
    public QSEncriptedValueProvider(ControllerContext controllerContext) : base(GetQSDictionary(controllerContext), Thread.CurrentThread.CurrentCulture)
    {
    }

    private static IDictionary<string, string> GetQSDictionary(ControllerContext controllerContext)
    {
        var dict = new Dictionary<string, string>();
        var req = controllerContext.HttpContext.Request;
        foreach (var key in req.QueryString.AllKeys.Where(x => x.First() == '_'))
        {
            var value = req.QueryString[key];
            dict.Add(key.Substring(1),Crypto.DecryptValue(value));
        }

        return dict;
    }

}

Derivamos de la clase base DictionaryValueProvider<string>. Esto lo que significa es que este value providers, tan solo dejará strings en “este sitio común”. Bueno, esto es lógico ya que en la querystring tan solo hay strings. Pero p.ej. otros value providers podrían dejar objetos enteros en este “sitio común” (como el que inspecciona la petición buscando datos en json).

Al derivar de esta clase obtenemos la facilidad de trabajar con un diccionario de cadenas (en nuestro caso de <string,string> ya que las claves siempre son cadenas). Básicamente, lo que dejemos en este diccionario es lo que se colocará en este “sitio común”. Y eso es lo que hace el método GetQSDictionary.

Mirando el código vemos que selecciona todas las claves de la querystring que empiecen por un subrayado, y por cada uno de ellas:

  • Desencripta su contenido
  • Guarda el valor desencriptado con la misma clave, pero quitando el subrayado del principio.

¡Ya estamos listos! Ahora tan solo debemos registrar la factoría de value providers. Para ello en el Application_Start podemos colocar la línea:

ValueProviderFactories.Factories.Add(new QSEncriptedValueProviderFactory());

¡Ya podemos probarlo!

Para ello me creo una vista con un link que tenga un parámetro encriptado:

@Html.ActionLink("Test", "Test", 
new {_foo=MvcApplication4.Crypto.EncryptValue("bar value")}, null)

El código fuente generado es el siguiente:

<a href="/Home/Test?_foo=aIGf0UYsNARiBaGZr3blRg%3D%3D">Test</a>

Podemos ver como el valor “bar value” ha sido encriptado. Y ahora viene lo bueno: en nuestro controlador declaramos la acción Test que recibe una cadena, y eso es lo que obtenemos:

image

En el controlador obtenemos ya el valor desencriptado! Fijaos además como el parámetro de la acción se llama foo (en lugar de _foo). Eso es porque en el value provider hemos eliminado el subrayado inicial. Por supuesto dado que también existe el value provider que procesa la querystring de forma “normal”, si en la acción declaramos un parámetro _foo obtendremos el valor encriptado.

Esta es una breve demostración del poder de los value providers. Por supuesto, deberíamos crearnos un conjunto de helpers para generar URLs encriptadas de forma más fácil, pero eso ya daría para otro post.

Un saludo!

PD: No he puesto el código de encriptar/desencriptar adrede porque hay muchas maneras de hacerlo. Yo en mi caso he usado uno que he encontrado en http://www.joshrharrison.com/archive/2009/01/28/c-encryption.aspx (aunque ello no es relevante para lo que cuenta este post).

Un comentario sobre “ASP.NET MVC: Encriptar la query string”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *