Our team was recently working on a test where we noticed that the application, which was a Single Page App (SPA) in front of a RESTful API, was using session cookies but did not have Cross Site Request Forgery (CSRF) tokens. When we discussed the issue with the development team, they indicated that in all their many years of experience writing API’s and fancy front ends, they had never used CSRF tokens with RESTful APIs.
The best source of information, maybe ironically, was this stack exchange post where a user posed the question and then answered it for themselves. We think they got it right, but it wasn’t quite clear enough for our client’s developers who were quick to cite CORS as a mitigation.
This lead us to an interesting point, which is that for certain types of requests, eg. XHR (Ajax) requests where they go through CORS and pre-flight checks, it is true that CSRF tokens are less important. However, simple POST requests do not go through pre-flight or CORS and therefore the CSRF token is still the only protection against CSRF.
The bottom line is that if you are using a cookie based session in your SPA and there are any endpoints that accept POST requests or GET requests that change data (an anti-pattern), then you should almost certainly have CSRF protection in place.
The whole idea behind CSRF is that the browser is a confused deputy and it will send your domain scoped cookie for domain-a.com to domain-a.com when a post to that domain happens, even if the form with the submit button is on domain-b.com. This works just the same if you are a rich Single Page App talking to a RESTful API, provided the authentication is based on the session cookie.
This is just about what Burp generated for a proof of concept, we translated it a bit from JSON to url encoding to make it work:
<html> <body> <script>history.pushState('', '', '/')</script> <form action="https://target.wtf/api/things" method="POST" enctype="text/plain"> <input type="hidden" name="thingy" value="Thing13"/> <input type="hidden" name="thingx" value="Thing14"/> <input type="submit" value="Submit request" /> </form> </body> </html>
The bottom line here is that we were surprised to find that developers working in an SPA thought CSRF was unnecessary. Remember, your attackers are not constrained to use your application.
Note: If you are using different authentication mechanisms, such as a key and secret with each request, where your RESTful endpoint is truly stateless and maybe the client is a command line application, it may be the case that you do not need CSRF protection. However, if you are building a web application, you should think twice because … you guessed it, many of these secrets end up in domain scoped cookies that the browser will happily send along with the request and will work fine for authentication.