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
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 "__RequestVerificationToken"
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.
Nice One...
ReplyDelete