I use c# MVC 3 with knockout and jquery.
I'll show you how to protect yourself using these technologies from csrf
attacks.
The reason why most manuals on the Internet are wrong is because many of us
are using the built in JsonValueProvider factory to hydrate a class. If you
put the validation attribute value in your JSON it will not work because
the ValidateAntiForgeryToken attribute is looking in the Request.Forms
collection for an attribute named __RequestVerificationToken. The following
code works nicely:
1) In your markup add: @Html.AntiForgeryToken()
2) Set up your javascript post method to work like this:
var verificationToken = {
__RequestVerificationToken: null
};
var token = $('[name=__RequestVerificationToken]');
if (token.length === 1) {
verificationToken.__RequestVerificationToken =
encodeURIComponent(token.val());
}
var options = {
headers: verificationToken,
url: url,
type: 'POST',
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify(data),
error: onErrorResponse,
success: onSuccessResponse
};
return $.ajax(options);
3) Create an HttpContextBase wrapper.
public class JsonAntiForgeryHttpContextWrapper : HttpContextBase
{
readonly HttpRequestBase _request;
private readonly HttpContext _httpContext;
public JsonAntiForgeryHttpContextWrapper(HttpContext httpContext)
{
_httpContext = httpContext;
_request = new
JsonAntiForgeryHttpRequestWrapper(httpContext.Request);
}
public override HttpRequestBase Request
{
get
{
return _request;
}
}
public override IPrincipal User
{
get
{
return _httpContext.User;
}
set
{
_httpContext.User = value;
}
}
}
4) Create a HttpRequest wrapper.
public class JsonAntiForgeryHttpRequestWrapper : HttpRequestWrapper
{
readonly NameValueCollection _form;
public JsonAntiForgeryHttpRequestWrapper(HttpRequest request)
: base(request)
{
_form = new NameValueCollection(request.Form);
if (request.Headers["__RequestVerificationToken"] != null)
{
_form["__RequestVerificationToken"] =
HttpUtility.UrlDecode(request.Headers["__RequestVerificationToken"]);
}
}
public override NameValueCollection Form
{
get
{
return _form;
}
}
}
5) Add a reference to your project for System.Web.Webpages.
6) Create a better validate anti forgery attribute.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
AllowMultiple = false, Inherited = true)]
public class ValidateAntiForgeryTokenByMethodAttribute :
FilterAttribute, IAuthorizationFilter
{
private readonly AcceptVerbsAttribute _verbs;
public ValidateAntiForgeryTokenByMethodAttribute(HttpVerbs verbs)
{
_verbs = new AcceptVerbsAttribute(verbs);
}
public void OnAuthorization(AuthorizationContext filterContext)
{
if
(_verbs.Verbs.Contains(filterContext.HttpContext.Request.GetHttpMethodOverride()))
{
var httpContext = new
JsonAntiForgeryHttpContextWrapper(HttpContext.Current);
AntiForgery.Validate(httpContext, "");
}
}
}
6) Apply the attribute to a controller class or method.
[ValidateAntiForgeryTokenByMethod(HttpVerbs.Post)]
There you have it. Took me a bit of time to figure this out. Hopefully it
saves you some.
Post by lucidguppyI am used to putting hidden fields in forms containing csrf tokens to
prevent csrf attacks. Now that I'm using knockout.js I don't download
forms anymore since its all one page. How do you guys prevent csrf in a
onepage knockout.js app?
Thanks,
Matt