Discussion:
CSRF
lucidguppy
2011-12-31 21:51:36 UTC
Permalink
I 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
Ben Hockey
2012-01-03 14:40:48 UTC
Permalink
assuming that you now use a POST via xhr to submit your form data, one
option is that you would include a "token" as part of the xhr to indicate
to the server that the request was from a valid client. this "token" is
usually a piece of data from a cookie (session id is typical). when the
client makes a request, the server can see the cookie included as part of
that request and then needs to make sure that the data in the cookie
(automatically included in the request) matches some piece of data included
in the body (or a header) of the request (needs to be explicitly included
by the client making the request) - similar to the idea of the hidden
field. only a valid client will be able to have both of these pieces in
their request.

make sense?

ben...
Wim Praet
2012-10-19 08:31:54 UTC
Permalink
I noticed the knockout (2.1.0) source code contains:

fieldsIncludedWithJsonPost: ['authenticity_token',
/^__RequestVerificationToken(_.*)?$/],

Should this be used to prevent CSRF attacks? If not, what is this field
ment for?

-Wim.
Post by lucidguppy
I 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
m***@public.gmane.org
2012-10-22 22:14:35 UTC
Permalink
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 lucidguppy
I 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
Loading...