Reader Level:
ARTICLE

Best Way to Prevent Cross Site Request Forgery Attacks in MVC 4

Posted by Abhimanyu K Vatsa Articles | ASP.NET MVC March 15, 2013
In this article you will learn what Cross Site Request Forgery (CSRF) is and is the best way to prevent such attacks. After a quick introduction about CSRF I will show you an example where the attacker will change the profile information with one click.
  • 0
  • 0
  • 9981

Introduction


Cross Site Request Forgery (CSRF or XSRF) is a form of attack in which the user authenticates themselves on any website and somehow navigates to another website setup and hosted by any attacker who gets the user to post a form back to the original website containing the information that the attacker specifies.
 

I recorded a very short video clip to show you how an attacker can trick and modify your important information. 




Preventing CSRF Attacks

 

ASP.NET MVC provides a set of anti-forgery helpers to help preventing such attacks. We use a user-specific token as a hidden field on the form and an attribute applied to the controller action that checks that the correct value was submitted with each POST request.

 

Here is how I put AntiForgeryToken in the Edit form:

 

@using (Html.BeginForm())

{

    @Html.ValidationSummary(true)

    @Html.AntiForgeryToken()

    <fieldset>

        <legend>Profile</legend>

        @Html.HiddenFor(model => model.ProfileId)

        <div class="editor-label">

            @Html.LabelFor(model => model.Name)

        </div>

        <div class="editor-field">

            @Html.EditorFor(model => model.Name)

            @Html.ValidationMessageFor(model => model.Name)

        </div>

 

And then an attribute [ValidateAntiForgeryToken] applied to the controller action:

 

    [HttpPost]

    [ValidateAntiForgeryToken]

    public ActionResult Edit(Profile profile)

    {

        if (ModelState.IsValid)

        {

            db.Entry(profile).State = EntityState.Modified;

            db.SaveChanges();

            return RedirectToAction("Index");

        }

        return View(profile);

    }

 

Now run the application and when you view the HTML source code, you'll see the following hidden input:

 

MVC2.jpg

 

At the same time, it also issues a cookie with the value encrypted. When the form POST occurs, it compares the issued cookie value and requests a verification token value on the server and ensures that they match; if they don't match then it displays the error "The required anti-forgery form field "_RequestVerificationToken" is not present.".

 

MVC3.jpg

 

So, by using @Html.AntiForgeryToken() on the form and the ValidateAntiForgeryToken attribute with the controller action we can avoid CSRF attacks easily and it works great in a typical form post to an action method.

 

We also POST our forms via jQuery $.post() calls, so how to prevent here? Please read on.

 

There is a great blog post Preventing CSRF With Ajax by Phil Haack on this title where he discussed prevention by creating some wrapper classes, I recommend that you also read it.

 

Here I came up with a very simple approach to get it done with just a single line of jQuery code. Before showing the code, I would like to show you something relevant to typical form posting.

 

As I said earlier, a POST request is only successful when the @Html.AntiForgeryToken() value is on the view page and the ValidateAntiForgeryToken value on the controller method both matches. Here is a screenshot of that showing a POST request sending the RequestVerificationToken to the controller method.

 

MVC4.jpg

 

Now, the only thing I need to do when the POST is made by jQuery $.post() calls is to send the RequestVerificationToken to the controller method. Okay, time to see my approach.

 

  <script type="text/javascript">

    $('#Save').click(function () {

    var token = $('input[name="__RequestVerificationToken"]:nth-child(2)').val();

    var url = "/Profile/CreateProfile";

    var name = $("#Name").val();

    var email = $("#EmailId").val();

    var about = $("#AboutYourself").val();

    $.post(url, { Name: name, EmailId: email, AboutYourself: about, __RequestVerificationToken: token }, function (data) {

        $("#msg").html(data);

    });

})

</script>

 

I underlined the magical line above that will search for the 2nd appearance of RequestVerificationToken on the view page and the $.post() call will send the token to the controller method, here it is:

 

MVC5.jpg

 

In the example above you can clearly see I'm able to send the "RequestVerificationToken" back to the controller method for verification and it works fine.

 

From the jQuery code above you can ask, why use nth-child(2), "two" second appearance. And the answer is, we have two Request Verification Tokens on the form in total, here is a screenshot:

 

MVC6.jpg

 

If you use nth-child(3) that is not available instead of nth-child(2), you will get the following error: "The required anti-forgery form field &quot;__RequestVerificationToken&quot; is not present.".

 

MVC7.jpg


So, you can see this concept works great. I don't know how useful this concept will be? Comments Please.

COMMENT USING

Trending up