Cross-site scripting (XSS) is the number one security
issue on the web. Your application can be vulnerable to cross-site request
forgery (CSRF) attacks not because you the developer did something wrong, but
simply because of how the whole Web is designed to work. Basically web browsers
allow GET and POST requests to be made between different web sites. Cross-site
request forgery (CSRF) occurs when a user visits a malicious web page that
makes their browser send requests to your application that the user did not
intend. This can be done with the src attribute of the IMG,
IFRAME or other tags and more complicated requests, including POSTs, can be
made using JavaScript. Because the browser always sends the relevant cookies
when making requests, requests like this appear to originate from an
authenticated user. The malicious site isn’t able to see the results of these
requests, but if create, update or delete functionality can be triggered; the
malicious site may be able to perform unauthorized actions.
Example of CSRF works:
Here is your controller class (“ResultsController”). The user goes
to Index(), which renders the form. Now they post that form to view the results
([HttpPost] method), which can do some useful staff i.e., modify user details,
bank accounts details etc. Still now all looks good.
public class ResultsController : Controller
{
//
// GET: /Results/
public ActionResult
Index() { return View(); }
[HttpPost]
public ActionResult
Index(ResultsModel model)
{
//do some staff
return View(model);
}
}
But that controller is easily target
for CSRF. How? Let’s check the html generated by the controller below.
<form method="post" action="/results">
User name: <input type="text" value="someusername"
name="UserName"
id="UserName"/>
Email
address: <input
type="text"
value="email@email.com"
name="Email"
id="Email"/>
<input type="submit" value="Update">
</form>
The following shows the “HTTP POST”
that is sent to the website (URL: http://localhost:51582/results). Here you can
notice the following things:
- We are posting to a well known URL, with or without sending a cookie.
- .ASPXAUTH, which lets the site know that user is already logged in or not
- We are posting some data (UserName=someusername&Email=email%40email.com)
The code is very much simple. Just
creating a form and posting the data to do some useful works. So why we are
fighting for CSRF? Now check below. Another guy can write the following piece
of code:
<html>
<head>
<title></title>
</head>
<body>
<form method="post"
name="csrf"
action="http://localhost:51582/results">
<input type="hidden"
name="UserName"
value="someusername"
/>
<input type="hidden"
name="Email"
value="hereis@your.email"
/>
</form>
<script type="text/javascript">
document.csrf.submit();
</script>
</body>
</html>
That person just did nothing only
create a HTML page hosted somewhere in the world with two input types, the same values and write a small script
to post the form and he can successfully update those things.
How to block CSRF:
We can do the following things:
- We can check the incoming request's “Referer” header to prevent this attack. That posted HTML is exactly the same as the form is created except the “Referer” because it is hosted somewhere in the world not in that server. But some proxy server, some people can disable their browser’s “Referer” header for privacy. So this is not a good solution at all.
- We can put a token as a hidden filed inside the form & then check that the correct value was submitted or not. It is good to use a random generated value while submitting the form. So that third party cannot forge the valid form post.
The AntiForgeryToken helpers:
ASP.Net
MVC introduces html helpers to detect and block CSRF using tokens. AntiForgeryToken
is built in validation and simple to add in the form (it is a hidden input
element with token). We need to add an @Html.AntiForgeryToken() into the form.
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div>
UserName: @Html.TextBoxFor(m
=> m.UserName)
Email: @Html.TextBoxFor(m
=> m.Email)
</div>
<input type="submit"
value="Update"
/>
}
This will generate a token and
writes it inside the form. HTML will be like this:
<form method="post" action="/results">
<input type="hidden" value="TKjmpi-KfyhZZf79x-v0BzIN6W-hDL9DhUAZJE5RffDw6ZRDGMPajlrGWSV8a7oP5oXrNXGw1riZ7FE86CO3to6i2rfpZ6_KoAHWEOUCwmFlL6-beCKZYKbf1pxL3xLnnEQ4qrTggZoDrxwPrB0AeEFeu8wJukwL0SB2iwi55oU1"
name="__RequestVerificationToken">
<div>
UserName:
<input type="text" value="" name="UserName"
id="UserName">
Email: <input type="text" value="" name="Email"
id="Email">
</div>
<input type="submit" value="Update">
</form>
This (AntiForgeryToken) generated a
visitor cookie name “
__RequestVerificationToken”, with the same value
as the random hidden value.
Now, to validate the incoming form
post, need to add the “[ValidateAntiForgeryToken]” to your action
method.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult
Index(ResultsModel model)
{
//do some staff
return View(model);
}
This will validate the token, when
the form post is submitted, compares the cookie value to the submitted
verification token.
The simple and excellent mechanism of AntiForgeryToken:
a. The server writes
tokens to cookie and inside the form.b. When the form is
submitted to server, token in cookie and token inside the form are sent in the
HTTP request.c. Server validates
the tokens.
Multiple forms in application & wrapper class:
To protect multiple forms in one
controller we can easily avoid a large number of “[ValidateAntiForgeryToken]” attributes. We
can use the following wrapper class, where HTTP verbs can be specified:
[AttributeUsage(AttributeTargets.Class
| AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ValidateAntiForgeryTokenWrapperAttribute : FilterAttribute, IAuthorizationFilter
{
private readonly ValidateAntiForgeryTokenAttribute _validator;
private readonly AcceptVerbsAttribute _verbs;
public ValidateAntiForgeryTokenWrapperAttribute(HttpVerbs verbs)
: this(verbs, null) { }
public ValidateAntiForgeryTokenWrapperAttribute(HttpVerbs verbs, string
salt) {
this._verbs = new AcceptVerbsAttribute(verbs);
this._validator = new
ValidateAntiForgeryTokenAttribute() { Salt =
salt };
}
public void
OnAuthorization(AuthorizationContext
filterContext) {
string httpMethodOverride =
filterContext.HttpContext.Request.GetHttpMethodOverride();
if (this._verbs.Verbs.Contains(httpMethodOverride,
StringComparer.OrdinalIgnoreCase))
{ this._validator.OnAuthorization(filterContext); }
}
}
We can call that like following:
[ValidateAntiForgeryTokenWrapper(HttpVerbs.Post)]
public class ResultsController : Controller
{
// All GET: actions,
which are not affected.
// All POST: actions,
those requests are validated.
}
Add the token in the AJAX request:
When AJAX Post, we can include the
verification token manually. We can simply add the parameter “__RequestVerificationToken”
with the token from the form as follow:
$.ajax({
type:
"POST",
url:
URL, //this will be the url
data:
{ value: value, __RequestVerificationToken: getVeryficationToken() },
success: function (result) {
// Success
}
});
function getVeryficationToken() {
var inputElements = $("input");
for (var i = 0; i
< inputElements.length; i++) {
var inputElement = inputElements[i];
if (inputElement.name === "__RequestVerificationToken")
{
return inputElement.value;
}
}
return null;
}
Some helpful articles:


















