Reputation: 325
I have a project where a Play Framework app serves as an API backend for a Chrome extension. Since I don't specify any filters for the Play Framework project, it enables CSRF protection (via CSRFFilter) by default which may or may not be suitable for my situation.
In the Chrome extension, I first make a jQuery AJAX call to sign in so that the Play Framework server sets a user token cookie (which will be used to identify the user in all subsequent API calls):
$.ajax({
method: 'POST',
url: serverUrl + "api/google_sign_in",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({ google_id_token: token }),
success: function (response) {
onSuccess();
},
error: function (req, status, error) {
onError(error);
}
});
The call succeeds and the user token is set as a cookie. So far so good. However as soon as I try to make any other API call using a similar jQuery AJAX call, Play Framework complains that a CSRF token is missing, presumably because now there's a cookie set that needs to be protected:
[CSRF] Check failed because no token found in headers for /api/get_status
And the call fails. From my very limited understanding of CSRF protection, the server is supposed to include the CSRF token in some generated HTML at some point? But in this case there are just pure API calls that return JSON. Am I expected to explicitly specify the CSRF token in the JSON returned from the server, and then manually include it as a header in following AJAX calls on the Javascript side?
Also, do I need CSRF protection for this type of API backend? Or can I disable it altogether?
Thanks, Nir
Upvotes: 1
Views: 3223
Reputation: 15
I don't like the idea of removing XSS protection from my route. The official documentation states :
If you are making requests with AJAX, you can place the CSRF token in the HTML page, and then add it to the request using the Csrf-Token header.
I found this post giving a very nice way to include the CSRF Token in your ajax requests. Be sure to read it as the following only explains the surface of the complete answer.
First be sure to add the @AddCSRFToken to your controller's methode that serves the page containing the JS posting your ajax request. This will make the token available from your JS script.
Then in the Scala Template (TWIRL) add the following code in your scripts :
<script type="text/javascript">
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader('Csrf-Token','@helper.CSRF.getToken.value');
}
});
</script>
This will tell JQuery to pass on the Csrf token to your ajax requests and play will not interfere.
$.post({
//Don't forget .url() for the js reverse routing ;)
url: jsRoutes.controllers.MyController.homePage().url,
data: {
armyId : data[i].id,
position : {
x: hex.position.x,
y: hex.position.y }
},
dataType: "json"
})
I hope this helped !
Upvotes: 0
Reputation: 1621
According to the official documentation on the CSRF filter, the CSRF filter is enabled by default for pretty much any non-GET, HEAD, or OPTIONS request (e.g. POST or PUT) where there is some sort of cookie/authorization header and the CORS filter is not configured to trust the origin of the request. You have a few options to fix this:
This one is pretty easy. Go into your routes file and put + nocsrf
in the line above each route you want to exempt from CSRF protection. This works best when you don't have a million API endpoints to exempt or you're not building a SPA/public API.
Set up the CORS filter to accept requests from multiple domains and it will automatically bypass CSRF.
For this kind of request where the user isn't directly using the website and it can be assumed that, if they have a valid token, they're correctly authenticated, you can just disable CSRF like you suggest and you'll probably be fine. Note that CSRF does make your application more secure but only in ensuring that requests cannot be forged from another website. Because it seems like your API is going to be called from pretty much everywhere, that does not apply to your project. I would, however, use it on any forms and controls on the backend service if there are any.
EDIT: According to this blog, you should still use CSRF if you have to worry at all about XSS. Token-based authentication is immune to CSRF but you can still get attacked by XSS if you're not careful (and the default assumption is that nobody is careful enough, and could use a little extra security). Make sure the client can get the CSRF token when making requests and have it stored separately from their main login token. Then authenticate requests using both on the server.
Upvotes: 1