I am planning on building an application with a decoupled front-end and back-end (using only ajax requests). I do not allow cross-site ajax requests. Can I generate a csrf token with an ajax call, by adding an API like /csrf which returns something like: {csrf: 'token'} The following site says I absolutely cannot do this: https://github.com/pillarjs/understanding-csrf
Make sure CSRF tokens can not be accessed with AJAX! Don't create a /csrf route just to grab a token, and especially don't support CORS on that route!
Is there a specific reason why? I understand the reasoning behind CORS - that is disabled, but is there any inherent security risk in providing the csrf token via ajax?
It's not a security risk, here's why:
Why you would you even want to expose CSRF tokens via AJAX?
From a usability perspective, if you don’t provide a way to get CSRF tokens via AJAX, your website is probably going to be hellish to use because anyone who has multiple tabs open is going to run into errors about mismatching CSRF tokens.
On a website that uses CSRF tokens it’s a much nicer experience for users if you have a hook on forms to fetch the latest CSRF token before submitting a POST request.
A way to easily fetch CSRF tokens is also required for modern Single Page Application websites (e.g. universal sites built using React or Angular), which benefit from projection given by CSRF tokens just like traditional server-rendered only based sites.
CSRF tokens are tied to sessions
CSRF tokens are tied to sessions — even if you are not explicitly logged in, it still needs to be tied to a session tracked between the client and server.
If you have an HTTP Only cookie for your session token (which is best practice and prevents XSS attacks from stealing it as well as helping protect you from CSRF attacks) no one can read your CSRF token from a request via remote domain (even if the browser doesn’t enforce CORS!).
Cross-Origin Resource Sharing
CORS bit come ups a lot in discussions like this but it’s actually a bit of a red herring.
The security really comes down to using an HTTP Only cookie for your sesison cookie and having a same domain policy for the cookie with your session token — which all browsers have supported since the dawn of time (with the edge case caveat of MSIE exposing cookies to subdomains where other browsers don’t).
If some JavaScript on a remote site can’t read your session token — which it can’t if it’s in an HTTP Only cookie on your domain — then it can’t read your CSRF token!
If use HTTP Only cookies and CSRF tokens am I secure against CSRF and XSS attacks?
You should be safe from CSRF attacks and you will have limited protection from some types of XSS attacks, but XSS is still a risk.
If someone finds a way to use XSS to execute arbitrary JavaScript on your website and create requests that come from your domain then neither CSRF, HTTP Only cookies or session fingerprinting will protect you at that point — any action performed could be made to look just like it was triggered by a user.
A CAPTCHA on requests would provide some additional protection, although for irreversibly destructive or potentially expensive operations, external confirmation of an action (e.g. via email, SMS, etc) is a good idea.
But isn’t it better to be safe than sorry?
If you are still worried an AJAX endpoint for CSRF might, somehow be a vector in a way you don’t understand, consider that a remote script that is able to execute a request from your domain and parse the response could equally just trigger a request to a page with a form on it and get the CSRF token from an value on a .
Equally, if someone is able to execute arbitrary JavaScript on your site and make and read requests back as the current user they are targeting, it would be a lot easier to just read the CSRF token from your DOM and extract it from a form already on the page than mess about with AJAX.
Summary
Use HTTP Only cookies and CSRF tokens to help protect against CSRF and XSS attacks.
When using HTTP Only cookies providing a method to fetch a CSRF token via AJAX is not exploitable.
Providing a way to fetch your CSRF token via AJAX is a good thing, that leads to better user experiences without compromising security.
Consider adding a CAPTCHA and/or external validation to confirm actions as appropriate, as protection against XSS.
You could alternatively not rotate a users CSRF token in a given session (some middleware actually does this to avoid dealing with the issue entirely) but it’s worth noting that is not typical behaviour for most CSRF token libraries.