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
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.