Best way to prevent Cross Site Request Forgery Attacks in MVC 4

In this article you will learn what CSRF is and best way to prevent such attacks. After a quick introduction about CSRF I will show you an example where attacker will change the profile information with one-click.

Introduction

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

I recorded a very short video clip to show you how 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 put user specific token as a hidden field on the form and an attribute applied to the controller action which checks the right value submitted with each POST request.

Here is how I placed AntiForgeryToken inside 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 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 HTML source code, you’ll see the following hidden input.


At the same time, it also issue a cookie with value encrypted. When the form POST occurs, it compares the issued cookie value and request verification token value on the server and ensures that they match, in case they don’t match will display ‘The required anti-forgery form field “_RequestVerificationToken” is not present.’ error.


So, by using @Html.AntiForgeryToken() on the form and ValidateAntiForgeryToken attribute with controller action we can avoid CSRF attacks easily and it works great in 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 you reading this also.

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 on typical form posting.

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


Now, the only thing I need to do when POST made by jQuery $.post() calls is send the RequestVerificationToken to 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 which will search of 2nd appearance of RequestVerificationToken on the view page and $.post() call will send token to controller method, here it is.


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

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


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


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

Hope this helps.

Comments

Post a Comment

Popular posts from this blog