Joven hacker sonriendo

Prevenir ataque CSRF haciendo uso de Tokens

Nuestros ethical hackers explican cómo validar la autenticidad de una petición mediante el uso de un token, para controlar la transferencia de información mediante los formularios de la aplicación con el fin de prevenir ataques de tipo Cross-Site Request Forgery.

Necesidad

Prevenir ataque CSRF haciendo uso de Tokens en C#.

Contexto

A continuación se describen las circunstancias bajo las cuales la siguiente solución tiene sentido:

  1. La aplicación está construida con el lenguaje de programación C#.

  2. Se utiliza la versión de .NET Framework 1.0 ó superior.

  3. El aplicativo hace uso de métodos GET ó POST para la transferencia de información a través de los formularios.

  4. Se debe tener acceso al código fuente para poder modificarlo y añadir la solución.

Solución

  1. La naturaleza de los ataques de tipo CSRF, consiste en aprovechar el exceso de confianza que se tiene con las acciones realizadas por parte del usuario en la plataforma, especialmente lo que respecta al ingreso de datos a través de los formularios; ya que no siempre se evalúa la legitimidad de éstos, y si realmente dichos actos se hicieron a conciencia, y no generados de manera indirecta desde un aplicativo ajeno a esta. Con el tiempo se han propuesto varias soluciones para frenar dichos ataques, entre las que están:

    1. Exigir una re-autenticación al usuario cada vez que el realice una acción en un formulario

    2. Comprobar a través del envío de un correo o SMS, la validez o legitimidad de una acción.

  2. Estas soluciones pueden resultar bastante exageradas e incómodas en muchos casos, por este motivo se propuso el uso de un mecanismo, que de manera automatizada, pudiera verificar las conexiones realizadas por usuario legítimos, haciendo un seguimiento a las acciones que estos realicen en los formularios donde ingresan los datos; tarea que se realiza haciendo uso de los llamados Tokens.

  3. A continuación, se muestra el código de una función que describe cómo al visitar una página con un formulario para ingresar datos, el sistema automáticamente genera un token, el cuál carga en un campo oculto del formulario, al igual que lo guarda en la sesión del usuario:

    ejemplo.asp
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    protected void Page_Load(object sender, EventArgs e) {
      //Se verifica que sea la primer vez que se carga el sitio
      if (!Page.IsPostBack) {
        //Se crea el token
        string CSRF_Token = System.Guid.NewGuid().ToString();
    
        //Se utiliza como identificador del Token dentro de la sesión, el nombre de la página en la que se encuentra  actualmente
        string page_name = System.IO.Path.GetFileName(HttpContext.Current.Request.Url.AbsolutePath);
        string page_token = page_name + "_ID";
    
        //Se guarda el Token generado en la sesión, con el identificador que se generó anteriormente para su futura busqueda
        HttpContext.Current.Session[page_token] = CSRF_Token;
    
        //Se asigna el Token al valor del campo oculto del formulario, que para efectos prácticos se le ha dado el nombre de Token
        Token.Value = CSRF_Token;
      }
    }
    
  4. La función Page_Load, es propia de la plataforma ASP.NET bajo código C#, y siempre se ejecuta cuando se carga la página a la que esté asociada; se verifica que sea la primer vez que se ingresa al sitio web y que no se ha ejecutado un refresco sobre la interfaz de la página, como ocurre cuando la página vuelve a cargar debido a que los datos que se ingresan son procesados en esta misma. Aunque si la interfaz actual se cierra, y vuelve a abrirse al tiempo, el Token se renovaría automáticamente. El motivo de utilizar el nombre de la página como identificador para la búsqueda del Token, es para poder generar uno por cada formulario al que se ingrese. A continuación se presenta el código encargado de verificar el Token, una vez se haya ingresado información a través del formulario:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    public void submit(object sender, EventArgs e) {
      //Se verifica que el valor del Token almacenado en sesión, sea igual al valor del campo oculto en el formulario
      string Page_Token = System.IO.Path.GetFileName(HttpContext.Current.Request.Url.AbsolutePath) + "_ID";
    
      if (Token.Value.ToString() != HttpContext.Current.Session[Page_Token].ToString()) {
        //De no ser los mismos, se cierra limpia y cierra la sesión, y se redirige hacia otro sitio web
        HttpContext.Current.Session.Abandon();
        HttpContext.Current.Session.Clear();
        Response.Redirect("...");
      } else {
        //De ser iguales los valores, se sigue con la ejecución de las instrucciones posteriores al envío de la información
      }
    }
    
  5. El campo oculto en el formulario tendría la siguiente forma:

    1
    <asp:HiddenField ID="Token" runat="server" />
    
  6. Y el elemento que generaría la ejecución de la verificación del Token, podría ser bien, un botón de las siguientes características:

    1
    <asp:Button ID="btn" Text="Submit" runat="server" OnClick="submit" />
    
  7. Para concluir, una característica que se podría añadir para darle más seguridad a la implementación del Token, es asignar un valor a la sesión, que permita saber el tiempo de vigencia del elemento en cuestión, de tal manera que al verificarlo, pasado cierto tiempo, este se descarte, y se tenga que generar uno nuevo. Aunque esto complicaría el hecho de generar un Token por cada formulario, pues existiría un tiempo asignado para cada uno, ocupando bastante espacio en la sesión. Si se piensa desarrollar un Token que sirva para todos los formularios del aplicativo, y que tenga un tiempo de vigencia, el código de asignación sería el siguiente:

    1
    2
    3
    4
    5
    6
      {
        string CSRF_Token = System.Guid.NewGuid().ToString();
        HttpContext.Current.Session["Token"] = CSRF_Token;
        HttpContext.Current.Session["Token_age"] = DateTime.Now;
        Token.Value = CSRF_Token;
      }
    
  8. Se realizaría una comparación de más, con el índice Token_age, para verificar si el Token ha pasado o no de cierto tiempo, el cual ya se establece directamente a conveniencia cuando se esté desarrollando el aplicativo.

Descargas

Puedes descargar el código fuente pulsando en el siguiente enlace:

  1. ejemplo.asp contiene todas las instrucciones C# del programa.




Haz un comentario

Estado de los servicios - Términos de Uso