Monday 3 August 2020

How to get out of CSRF attack by securing your AJAX requests by passing AntiForgeryToken

Why do we need Anti-Forgery Tokens

To help prevent CSRF attacks, ASP.NET MVC uses anti-forgery tokens, also called request verification tokens.

1.     The client requests an HTML page that contains a form.

2.     The server includes two tokens in the response. One token is sent as a cookie. The other is placed in a hidden form field. The tokens are generated randomly so that an adversary cannot guess the values.

3.     When the client submits the form, it must send both tokens back to the server. The client sends the cookie token as a cookie, and it sends the form token inside the form data. (A browser client automatically does this when the user submits the form.)

4.     If a request does not include both tokens, the server disallows the request.

 

Adding an Anti-Forgery Tokens is straight forward and easy To add the anti-forgery tokens to a Razor page, we can use the HtmlHelper.AntiForgeryToken helper method:

 Example:

 @using (Html.BeginForm("Create", "Customer")) {

    @Html.AntiForgeryToken()

}

 But in many cases programmers use AJAX to post data to server. an AJAX request might send JSON data, not HTML form data therefore the normal     @Html.AntiForgeryToken() method has nothing to do here so we need to use another workaround to fix this issue.

 If we are looking behind the scenes in Asp.Net MVC when we use @Html.AntiForgeryToken() Razor creates a hidden input field with name __RequestVerificationToken to store tokens.

 Here is an example html of hidden token

 <form action="/Customer/Create" method="post">

    <input name="__RequestVerificationToken" type="hidden"  

           value="6fGBtLZmVBZ59oUad1Fr33BuPxANKY9q3Srr5y[...]" />   

    <input type="submit" value="Submit" />

</form>

 

The work around is to Get the token before sending ajax request and Pass the token in the AJAX call.

 Step 1:

 var token = $('input[name="`__RequestVerificationToken`"]').val();

 Step 2:

 function createCustomer() {

 var customer = {     
    "FirstName": $('#fName').val(),
    "LastName": $('#lName').val(),
    "Email": $('#email').val(),
    "Phone": $('#phone').val(),
}; 
$.ajax({
    url: '/Customer/Create
    type: 'POST',
    data: { 
     __RequestVerificationToken:token,
     student: student,
        },
    dataType: 'JSON',
    contentType:'application/x-www-form-urlencoded; charset=utf-8',
    success: function (response) {
        if (response.result == "Success") {
            alert(Customer Created Succesfully!')
         }
    },
    error: function (x,h,r) {
        alert('Something went wrong')
      }
})
};
  
In the above example we are sending it via the data part of ajax request. We can also send it through header. 
 <script>

    @functions{

        public string TokenHeaderValue()

        {

            string cookieToken, formToken;

            AntiForgery.GetTokens(null, out cookieToken, out formToken);

            return cookieToken + ":" + formToken;               

        }

    }

 

    $.ajax("Customer/Create", {

        type: "post",

        contentType: "application/json",

        data: {  }, // JSON data goes here

        dataType: "json",

        headers: {

            'RequestVerificationToken': '@TokenHeaderValue()'

        }

    });

</script>
 
If you are passing it in the header then you can use the below custom antiforginary validator attribute, so that you will get more control over the process. 
 

namespace Web.UI.Security

{

    using System;

    using System.Configuration;

    using System.Web.Helpers;

    using System.Web.Mvc;

 

    /// <summary>

    /// This method will validate anti forgery token in ajax requests,

    /// we must include the token in header to get it validated correctly

    /// </summary>

    /// <seealso cref="System.Web.Mvc.FilterAttribute" />

    /// <seealso cref="System.Web.Mvc.IAuthorizationFilter" />

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]

    public class ValidateAjaxAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter

    {

        public void OnAuthorization(AuthorizationContext filterContext)

        {

            if (filterContext == null)

            {

                throw new ArgumentNullException("filterContext");

            }

 

            var httpContext = filterContext.HttpContext;

            var cookie = httpContext.Request.Cookies[AntiForgeryConfig.CookieName];

                //vaiadating the requst verification token in the request header

                AntiForgery.Validate(cookie != null ? cookie.Value : null, httpContext.Request.Headers["__RequestVerificationToken"]);

        }

    }

}

 

 

 

 

 


1 comment:

  1. Splendid! That was a fortune list of suggestions offered in the conversation. I understood how you had shown concern for enhancing your skill sets as a full-stack engineer. It definitely helps at improving the skills to have a promising career. The emerging platform, such as Eiliana.com, offers a wide range of projects to interested freelancers to work upon and accentuate their skill sets.

    ReplyDelete