Evitando JavaScript Injection en ASP.NET MVC

Hace unos días revisando un informe de sobre seguridad web, un ítem me llamo la atención porque es una de esas cosas que a uno se olvida de tenerlas en cuenta al momento de desarrollar un sitio. El mismo hablaba sobre los ataques de inyección de JavaScript.

Este tipo de ataque se denomina Cross-site scripting o de manera abreviada XSS:

XSS, del inglés Cross-site scripting es un tipo de inseguridad informática o agujero de seguridad basado en la explotación de vulnerabilidades del sistema de validación de HTML incrustado.

La idea de este post es ver de que forma podemos prevenir este tipo de ataques en aplicaciones ASP.NET MVC partiendo desde dos ópticas diferentes.

Antes que nada vamos a ver un poco mas en profundidad de que se trata JavaScript Injection y como se llevan a cabo este tipo de ataques. Para darnos una mejor idea supongamos que desde nuestro sitio web tenemos una caja de texto en la cual un usuario carga contenido y luego el mismo se visualiza en la página. Esta situación podría estar dejando abierta la posibilidad de un ataque de inyección de Javascript. Cómo es posible esto?… vamos a verlo con un simple ejemplo.

Lo primero sera crear una aplicación web tipo Twitter pero muy simplificada, en la cual vamos a permitir este tipo de ataques. Vamos a crear un controlador al cual vamos a llamar TweetController y a definir la siguiente entidad que formará parte del modelo de nuestra app y que se llamaremos Tweet:

1: public class Tweet
2: {
3:     public int TweetID { get ; set ; }
4:
5:    public string Usuario { get ; set ; }
6:
7:     public string Mensaje { get ; set ; }
8: }

Al momento de codificar la vista, la visualización de los items estará armada de la forma estandar:

1: <div class="tweetSection">
2:  <% foreach (var item in Model) {  %> 
3:     <span class="tweetUser">  <%= item.Usuario %></span>
4:    <br />
5:      <%= item.Mensaje  %> 
6:     <br />
7:     <br />
8:  <%   }  %>
9: </div>

Básicamente nuestra app esta conformada por un formulario donde se ingresa un nombre de usuario y un mensaje, el cual al momento de hacer el submit se grabara en una DB (para esto utilizaremos la librería EF) y luego se visualizara en la misma pantalla:

Interfaz de la aplicación

Interfaz de la aplicación

De la forma en que “dibujamos” el formulario, hemos dejado desprotegido nuestro sitio a los ataques de este tipo. Imaginemos que el usuario escribe un simple script de Javascript en nuestra caja de texto, por ejemplo, la ejecución de un mensaje de alerta informándonos que hemos sido atacados:

Cargando un script JS en el formulario

Cargando un script JS en el formulario

Una vez que “tweeteamos” nuestro contenido y volvemos a cargar la vista (que incluye la visualización del contenido de cada uno de los tweets) se estará ejecutando nuestro script:

Ataque de Javascript Injection

Ataque de Javascript Injection

Uno puede pensar que este tipo de ataques son inofensivos, sin embargo un hacker podría utilizar la inyección de JavaScript para realizar un ataque Cross-Site Scripting (XSS) robando información confidencial del usuario y enviando la misma a otro sitio web (dos ejemplos clásicos serían robar información de las cookies del navegador del usuario o información que se carga en un formulario que previamente a sido victima de Javascript Injection).

Cómo podemos evitar este ataque? Tenemos dos alternativas, la primera es utilizar el helper HTML.Encode el cual se encargara de renderizar el contenido de forma tal que no se ejecuten scripts que alguien pudo ingresar:

1: <div class="tweetSection">
2:  <% foreach (var item in Model) {  %> 
3:     <span class="tweetUser">  <%= item.Usuario %></span>
4:     <br />
5:      <%= Html.Encode(item.Mensaje)  %> 
6:     <br />
7:     <br />
8:  <%   }  %>

Ejecutamos y vemos que ya no se ejecuta nuestro script, ya que todo contenido reconocido como HTML a sido codificado de forma tal que se pueda visualizar como texto:

Utilizando el Helper Html.Encode

Utilizando el Helper Html.Encode

La segunda alternativa es codificar el contenido antes de grabarlo en la base de datos. Para esto podemos hacer uso del método Server.HtmlEncode que nos provee nuestro controlador:

1:             if (ModelState.IsValid)
2:             {
3:                 tweet.Mensaje = Server.HtmlEncode(tweet.Mensaje);
4:                 datos.Tweets.Add(tweet);
5:                 datos.SaveChanges();
6:             }

Utilizando este esquema ya no sería necesario utilizar el helper Html.Encode para visualizar el contenido (aunque no esta demás dejarlo). La desventaja de este enfoque es que los datos que se graban en la base de datos están codificados, lo cual no es del todo prolijo sobre todo si esta información se necesita visualizar en otro tipo de plataformas que no sean web. Observemos a continuación como se guardan los datos sin codificar (registro 2) y codificandolos (registro 3):

Registros de la base de datos

Registros de la base de datos

Para finalizar quería comentarles que utilizando el ViewEngine Razor los datos son “encodeados” automaticamente, sin necesidad de utilizar el helper Html.Encode y previniendonos de este tipo de ataques de entrada.  🙂

Quienes quieren el proyecto del ejemplo pueden descargarlo desde aquí: Projecto MvcJavascriptInjection

Anuncios