I have been facing serious problem with @Html.AntiForgeryToken()
. I have a register controller which had a create view to create/register new members. For that reason I used a @Html.AntiForgeryToken()
without using any SALT in my main submit form. Now I would like to validate user name if it is already exist on the database on the blur event of my user name textbox. For this validation I wrote a new controller named 'Validation' and wrote a method with a constant validation SALT:
[HttpPost]
[ValidateAntiForgeryToken(Salt = @ApplicationEnvironment.SALT)]
public ActionResult username(string log) {
try {
if (log == null || log.Length < 3)
return Json(log, JsonRequestBehavior.AllowGet);
var member = Membership.GetUser(log);
if (member == null) {
//string userPattern = @"^([a-zA-Z])[a-zA-Z_-]*[\w_-]*[\S]$|^([a-zA-Z])[0-9_-]*[\S]$|^[a-zA-Z]*[\S]$";
string userPattern = "[A-Za-z][A-Za-z0-9._]{3,80}";
if (Regex.IsMatch(log, userPattern))
return Json(log, JsonRequestBehavior.AllowGet);
}
} catch (Exception ex) {
CustomErrorHandling.HandleErrorByEmail(ex, "Validate LogName()");
return Json(log, JsonRequestBehavior.AllowGet);
}
//found e false
return Json(log, JsonRequestBehavior.AllowGet);
}
Method is working fine . I had checked with the HTTP Get annotation without the [ValidateAntiForgeryToken]
and it giving me the expected results.
I had googled and checked many of the given solutions none of these are working. For my validation controller I used another form in the same page and used a SALT in the Anti-forgery token.
Example: First anti-forgery token for the main submit form:
@using (Html.BeginForm("Create", "Register")) { @Html.AntiForgeryToken() @Html.ValidationSummary(true) ... }
Second anti-forgery token:
<form id="__AjaxAntiForgeryForm" action="#" method="post">
@Html.AntiForgeryToken(SALT)
</form>
and in the javascript I used this
<script type="text/javascript" defer="defer">
$(function () {
AddAntiForgeryToken = function (data) {
data.__RequestVerificationToken = $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val();
return data;
};
if ($("#LogName").length > 0) {
$("#LogName").blur(function () {
var user = $("#LogName").val();
var logValidate = "/Validation/username/";
//var URL = logValidate + user;
//var token = $('#validation input[name=__RequestVerificationToken]').val();
data = AddAntiForgeryToken({ log: user });
$.ajax({
type: "POST",
dataType: "JSON",
url: logValidate,
data: data,
success: function (response) {
alert(response);
}
});
});
}
});
</script>
In my firebug I got this :
log=admin&__RequestVerificationToken=NO8Kds6B2e8bexBjesKlwkSexamsruZc4HeTnFOlYL4Iu6ia%2FyH7qBJcgHusekA50D7TVvYj%2FqB4eZp4VDFlfA6GN5gRz7PB%2ByZ0AxtxW4nT0E%2FvmYwn7Fvo4GzS2ZAhsGLyQC098dfIJaWCSiPcc%2FfD00FqKxjvnqmxhXvnEx2Ye83LbfqA%2F4XTBX8getBeodwUQNkcNi6ZtAJQZ79ySg%3D%3D
as passed but in the cookie section I got a different cookie than passing one: Actual Cookie:
ws5Dt2if6Hsah rW2nDly P3cW1smIdp1Vau 0TXOK1w0ctr0BCso/nbYu w9blq/QcrXxQLDLAlKBC3Tyhp5ECtK MxF4hhPpzoeByjROUG0NDJfCAlqVVwV5W6lw9ZFp/VBcQmwBCzBM/36UTBWmWn6pMM2bqnyoqXOK4aUZ4=
I think this is because I used 2 anti-forgery tokens in one page. But in my mind I should use 2 because first one is generating for the submit to occur and next one is need to verify the validation. However, this is my guess and I think I am wrong and for this reason I need help from you guys.
Can anyone please explain the facts that should I use two anti-forgery or one?
Thank you all in advance....
AntiForgeryToken() Generates a hidden form field (anti-forgery token) that is validated when the form is submitted.
This is to prevent Cross-site request forgery in your MVC application. This is part of the OWASP Top 10 and it is vital in terms of web security. Using the @Html. AntiforgeryToken() method will generate a token per every request so then no one can forge a form post.
Adding an AntiForgeryToken generates a Cryptographically valid hash at the server end which is split and a part is added as a hidden field, whereas the rest goes into a cookie. When data is posted, the Cookie and the Hidden Field are both sent back and if they are missing or they don't match, the POST is rejected.
If the attacker forges a login page and gets the credentials that way, what's the point of using the token to protect the real login page? The attacker would be able to login anyway using the userand password that he got. This answer is dangerously wrong. The tokens ARE necessary.
Finally 8 hours of struggling gives me a solution.
First things first, yes there is no harm to use 2 anti-forgery tokens in the page. And second there is no need to match the cookies with the providing token. Providing token will be always different and will be verified in the server.
Anti-forgery token does not work if we use [HttpGet]
action verbs.. Because in the validation process of anti-forgery the token is validated by retrieving of the Request.Form['__RequestVerificationToken']
value from the request. ref : Steven Sanderson's blog: prevent cross...
Solution :
My modifies controller:
[HttpPost]
[ValidateAntiForgeryToken(Salt = @ApplicationEnvironment.SALT)]
//change the map route values to accept this parameters.
public ActionResult username(string id, string __RequestVerificationToken) {
string returnParam = __RequestVerificationToken;
try {
if (id == null || id.Length < 3)
return Json(returnParam, JsonRequestBehavior.AllowGet);
var member = Membership.GetUser(id);
if (member == null) {
//string userPattern = @"^([a-zA-Z])[a-zA-Z_-]*[\w_-]*[\S]$|^([a-zA-Z])[0-9_-]*[\S]$|^[a-zA-Z]*[\S]$";
string userPattern = "[A-Za-z][A-Za-z0-9._]{3,80}";
if (Regex.IsMatch(id, userPattern))
return Json(returnParam, JsonRequestBehavior.AllowGet);
}
} catch (Exception ex) {
CustomErrorHandling.HandleErrorByEmail(ex, "Validate LogName()");
return Json(returnParam, JsonRequestBehavior.AllowGet);
}
//found e false
return Json(returnParam, JsonRequestBehavior.AllowGet);
}
My first Form in the same page:
@using (Html.BeginForm("Create", "Register")) {
@Html.AntiForgeryToken(ApplicationEnvironment.SALT)
@Html.ValidationSummary(true)
....
}
My second Form in the same page:
**<form id="validation">
<!-- there is harm in using 2 anti-forgery tokens in one page-->
@Html.AntiForgeryToken(ApplicationEnvironment.SALT)
<input type="hidden" name="id" id="id" value="" />
</form>**
My jQuery to solve this thing:
$("#LogName").blur(function () {
var user = $("#LogName").val();
var logValidate = "/Validation/username/";
$("#validation #id").val(user);
**var form = $("#validation").serialize(); // a form is very important to verify anti-forgery token and data must be send in a form.**
var token = $('input[name=__RequestVerificationToken]').val();
$.ajax({
**type: "POST", //method must be POST to validate anti-forgery token or else it won't work.**
dataType: "JSON",
url: logValidate,
data: form,
success: function (response) {
alert(response);
}
});
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With