I'm designing the API for a group of sites. The sites are very similar (kind of like StackOverflow, SuperUser and ServerFault), and it makes sense for them to have a shared backend. Hence we decided to try and have a nice REST API as a backend, and a bunch of very-similar-but-different frontends consuming said API. The frontends should preferably be all static, but that's not a hard requirement if it turns out to be borderline impossible.
I'm working on designing that API now, and I'm worried about the security implications, particularly CSRF. From my basic understanding of CSRF attacks, they consist of two important components:
Being able to name the resource and the request body.
Tricking the user/browser into using ambient auth (like sessions) to make a request to that resource that looks authenticated.
A lot of the classic approaches to fixing CSRF attacks are based on the session. Since my REST API doesn't really do sessions, that both prevents a lot of the vectors and also pretty much all of the ways to fix them. For example, double submitting doesn't make sense because there's nothing to double submit.
My initial approach involved attacking part 2 of a CSRF attack. If I authenticate all the requests (say using HTTP Basic Auth), and the browser doesn't keep those credentials stored (e.g. some JS made the request), only the JS that has the credentials can make the request, and we're done. The obvious downside is that the app needs to know the user's credentials. The other slightly less obvious downside is that if I want to store credentials securely on the API end, then verifying a password should take a fixed, non-trivial amount of time. If verifying a password securely takes 100ms, then every other request is going to take at least 100ms + eps, and it's going to take some darn clever clientside trickery to make that not feel slow. I might be able to cache that (since the credentials will always be the same), and if I'm very careful I might manage to do that without introducing a timing vulnerability, but that sounds like a hornet's nest.
OAuth 2.0 seems a bit over the top, but I guess it might be the best solution after all, lest I end up implementing it poorly. I suppose I could do the HTTP Basic Auth thing for now, and move to OAuth when we have third party app developers.
There's a bit of an impedance mismatch with OAuth. OAuth really wants to help apps access stuff on another app, basically. I want users to sign up on one of the frontends, before such an account even exists.
I've also considered attacking point 1 by making the URLs randomized -- ie adding tokens to a query string. This would certainly work and it's very close to how the traditional randomized token in a form works, and given HATEOAS it should even be fairly RESTful, although this raises two questions: 1) where do you start? Is there a mandatory API start point where you do log in using HTTP Basic Auth? 2) How much would it make app developers happy if they can't predict a URL up front, HATEOAS be damned?
I have seen How to prevent CSRF in a RESTful application?, but I disagree with the premise that randomized URIs are necessarily unRESTful. Also, that question doesn't really have any satisfactory answers, and doesn't mention OAuth. Also, the session double submit solution is invalid, as I've mentioned above (different domain for the static frontend than the API endpoint).
I realize that what I'm fundamentally trying to do here is trying to allow cross-site requests from one domain and disallow them from the other, and that's not easy. Surely there has to be some reasonable solution?
If our project requires CSRF protection, we can send the CSRF token with a cookie by using CookieCsrfTokenRepository in a custom WebSecurityConfigurerAdapter. After restarting the app, our requests receive HTTP errors, which means that CSRF protection is enabled.
Enabling cross-site request forgery (CSRF) protection is recommended when using REST APIs with cookies for authentication. If your REST API uses the WCToken or WCTrustedToken tokens for authentication, then additional CSRF protection is not required.
REST APIs can be vulnerable to CSRF attacks; how to prevent exploits : r/netsec.
A CSRF token is by definition "per user state" and therefore not RESTful. Most API's break their "per user state" requirements for the purposes of security, and require a CSRF token passed as an HTTP header parameter.
There are other ways of preventing CSRF. Checking the referer is not as strong as a CSRF token, but it is RESTful and VERY unlikely to be undermined. Keep in mind that the lack of a referer should be considered a failed request.
XSS can be used to bypass both token based CSRF prevention and referer based CSRF prevention.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With