Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using MVC3's AntiForgeryToken in HTTP GET to avoid Javascript CSRF vulnerability

In regards to this Haacked blog, I'm hesitant to implement the proposed anti-JSON GET hijacking solutions since

  1. The recommended solutions to mitigating JSON hijacking involve non-REST-full JSON POSTs to GET data

  2. The alternate solution (object wrapping) causes problems with 3rd party controls I don't have source-code access to.

  3. I can't find a community-vetted implementation that implements the Alternative Solution (listed below) on how to compose the security token, or securely deliver it within the webpage. I also won't claim to be enough of an expert to roll my own implementation.

  4. Referrer headers can't be relied upon

Background

This blog describes a CSRF issue regarding JSON Hijacking and recommends using JSON POSTs to GET data. Since using a HTTP POST to GET data isn't very REST-full, I'd looking for a more RESTfull solution that enables REST actions per session, or per page.

Another mitigation technique is to wrap JSON data in an object as described here. I'm afraid this may just delay the issue, until another technique is found.

Alternative Implementation

To me, it seems natural to extend the use ASP.NET MVC's AntiForgeryToken with jQuery HTTP GETs for my JSON.

For example if I GET some sensitive data, according to the Haacked link above, the following code is vulnerable:

$.getJSON('[url]', { [parameters] }, function(json) {
    // callback function code
});

I agree that it isn't RESTfull to GET data using the recommended POST workaround. My thought is to send a validation token in the URL. That way the CSRF-style attacker won't know the complete URL. Cached, or not cached, they won't be able to get the data.

Below are two examples of how a JSON GET query could be done. I'm not sure what implementation is most effective, but may guess that the first one is safer from errant proxies caching this data, thus making it vulnerable to an attacker.

http://localhost:54607/Home/AdminBalances/ENCODEDTOKEN-TOKEN-HERE

or

http://localhost:54607/Home/AdminBalances?ENCODEDTOKEN-TOKEN-HERE

... which might as well be MVC3's AntiForgeryToken, or a variant (see swt) thereof. This token would be set as an inline value on whatever URL format is chosen above.

Sample questions that prevent me from rolling my own solution

  1. What URL format (above) would you use to validate the JSON GET (slash, questionmark, etc) Will a proxy respond to http://localhost:54607/Home/AdminBalances with http://localhost:54607/Home/AdminBalances?ENCODEDTOKEN-TOKEN-HERE data?

  2. How would you deliver that encoded token to the webpage? Inline, or as a page variable?

  3. How would you compose the token? Built in AntiforgeryToken, or by some other means?

  4. The AntiForgeryToken uses a cookie. Would a backing cookie be used/needed in this case? HTTP Only? What about SSL in conjunction with HTTP Only?

  5. How would you set your cache headers? Anything special for the Google Web Accelerator (for example)

  6. What are the implications of just making the JSON request SSL?

  7. Should the returned JSON array still be wrapped in an object just for safety's sake?

  8. How will this solution interop with Microsoft's proposed templating and databinding features

The questions above are the reasons I'm not forging ahead and doing this myself. Not to mention there likely more questions I haven't thought of, and yet are a risk.

like image 678
makerofthings7 Avatar asked Feb 06 '11 17:02

makerofthings7


People also ask

What does HTML AntiForgeryToken () do?

AntiForgeryToken()Generates a hidden form field (anti-forgery token) that is validated when the form is submitted.

What is AntiForgeryToken in Web API?

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.


2 Answers

The Asp.net MVC AntiForgeryToken won't work through HTTP GET, because it relies on cookies which rely on HTTP POST (it uses the "Double Submit Cookies" technique described in the OWASP XSRF Prevention Cheat Sheet). You can also additionally protect the cookies sent to the client by setting the as httponly, so they cannot be spoofed via a script.

In this document you can find various techniques that can be used to prevent XSRF. It seems the you described would fall into the Approach 1. But we have a problem on how to retrieve the session on the server when using Ajax HTTP GET request since the cookies are not sent with the request. So you would also have to add a session identifier to you action's URL (aka. cookieless sessions, which are easier to hijack). So in order to perform an attack the attacker would only need to know the correct URL to perform the GET request.

Perhaps a good solution would be to store the session data using some key from the users SSL certificate (for example the certs thumb-print). This way only the owner of the SSL certificate could access his session. This way you don't need to use cookies and you don't need to send session identifiers via query string parameters.

Anyway, you will need to roll out your own XSRF protection if you don't want to use HTTP POST in Asp.net MVC.

like image 196
m0sa Avatar answered Oct 08 '22 12:10

m0sa


I came to this problem and the solution was not so trivial however there is a fantastic blog to get you started this can be used with get and post ajax.

http://johan.driessen.se/posts/Updated-Anti-XSRF-Validation-for-ASP.NET-MVC-4-RC

If you place the following in the global name space all your post/gets can take advantage having an anti forgery token and you don't have to modify your ajax calls. Create an input element in a common page.

<form id="__AjaxAntiForgeryForm" action="#" method="post">@Html.AntiForgeryToken()</form>

The following javascript will read the anti forgery tokken and add it to the request header.

// Wire up the global jQuery ajaxSend event handler.
$(document).ajaxSend(namespace.ajax.globalSendHandler);

// <summary>
// Global handler for all ajax send events.
// </summary>
namespace.ajax.globalSendHandler = function (event, xhr, ajaxOptions) {
    // Add the anti forgery token
    xhr.setRequestHeader('__RequestVerificationToken', $("#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]").val());
};
like image 33
Marko Avatar answered Oct 08 '22 12:10

Marko