NodeJS CSRF Protection with Express, GraphQL and CORS enabled - javascript

I am creating a web service very much like shopify but using only JS. Nodejs express for an API with GraphQl and Cors enabled. VueJS for frontend. My authentication is running with JWT. But I have things like anonymous checkout so i need a CSRF protection. The thing is my API is not a router. My router is in the frontend and im only getting the data I need via Graphql through Axios calls to the API. I took a look at the csurf module and tried it out but currently the way im getting a CSRF token to the frontend is with a /getCSRFToken endpoint on the API which i've read is not a good practice and the other thing is It's enabled to access to everyone because of the CORS enabled.
This is the main source of information I have: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
I don't know how to exactly set up the CSRF protection without having am API route for getting the CSRF token and sending it as a cookie through the response and generally make the whole thing secure with the best practices.
I was also thinking about restricting access to the API only for the domains of the shops that are in the system but don't now if that will be any good either.
Any suggestions are welcome

You can generate the cookie client side (using window.crypto), then have the JS read it and send it in a header, the server simply has to verify that they match. But this is vulnerable to the fact the cookie is not HttpOnly (because your JS needs to read it!). For this reason, this method is not best practice, but it is better than nothing.
It also does not prevent users from issuing requests from curl and such once they figure out that they only need to provide a matching cookie and header, but they still cannot issue requests on behalf of other users unless they have the target users authorisation credentials.
There actually isn't anything wrong with having an API route which generates a token per request, although it does result in doubled request density (you need a new token for each request!). The reason for this is that an attacker cannot read the response from an external site (CORS will prevent this). Then you are not vulnerable to any cookie exploit, since you never store a cookie in the first place.
Edit: I see you hint at having CORS * enabled for this endpoint to be public. If your API really is public then you'll have probably better off using OAuth2/JWT authentication instead, this way CSRF becomes irrelevant, since the authentication does not come from cookies.
Trying to keep a value across multiple requests encounters difficulty with history functionality, so it's recommended to either use a token per request or...
You could also store a cookie from the getCsrfToken() request and keep it valid for some time, but make it HttpOnly, since it was issued by the API, the API will be responsible for making sure that it is receiving a valid CSRF token.
The issue with both of the above is that if you want true anonymity then you can't tie these tokens to a particular user, so one user could avoid the CSRF checks on behalf of another by using their own CSRF token!
If you can come up with some way around that whilst maintaining anonymity then the server can check the validity of the tokens that it is receiving, such that one user cannot use their token on behalf of another.
Your last idea (assuming that you want true anonymity) is probably the best. Provided that the user agent is trustworthy, the referer and Origin headers cannot be tampered with, so if you are happy to lock down your API to only the domains which your JS is running on, then doing a referer/Origin check server side will not be easily worked around by an attacker. This isn't best practice, but is practically effective.
Again, curl requests and such can be issued freely, but they can only be issued on behalf of another user if the attacker has the user's authorisation credentials.
The last thing to note is that CSRF is an alternative attack vector to XSS, but if you have XSS vulnerabilities, then CSRF defences usually become obsolete, so make sure that you defend XSS first, before implementing CSRF defence.

Related

Mitigate CSRF attacks in React.js

tl;dr
I want to pick your collective brains for my psuedo logic for preventing both XSS and CSRF attacks at the same time when dealing with authenticated sessions in SPA frontends like React (but this applies to any modern frontend pattern where most or all data used in the frontend is populated through incremental XMLHttpRequests that pass API responses to JavaScript logic rather than relying on server side rendering of HTML/backend redirects for the majority of a website).
The reason why I am rehashing this after observing an abundance of answers online is because all the search results on the subject I have encountered suffer one or both of the following problems:
Articles target the aforementioned SSR way of doing things like injecting a CSRF Token as a hidden input in an HTML form
Articles with solutions that only mitigate XSS attacks or CSRF attacks, but not at the same time
I believe there are some pure React/Express based node modules which may or may not solve these problems. But I want to understand a solution at a technical level to be able to implement this in a React SPA + Go backend setup, or any other backend that isn't Node based. I am also making the post because of the lack of public forum to point out security flaws in other approaches I have seen.
tl;dr end
This video argues about why this is might be a non-issue, but is also the starting point of how I thought about the problem.
The problem boils down to the following: an XSS vulnerability is quite trivial to fix by utilizing a HttpOnly cookie inside a modern frontend framework. But this approach alone is still vulnerable to CSRF attacks.
The traditional approach to mitigate CSRF attacks via inserting a hidden input field into server side rendered HTML does not apply to modern SPA's.
The approach I think would prevent both XSS and CSRF is by combining both:
Modern frontend framework + HttpOnly cookie to prevent XSS
CSRF Token passed in every XMLHttpRequest to protected endpoints as a header.
I partially base my logic on this article, but since this project isn't on Github or Medium, I can't find any criticisms of potential security flaws, or anyone praising it as sound.
Here is the detailed approach on how I think you could mitigate XSS and CSRF attacks:
A React rendered login page (/login) you log into via true HTML form submit (not via XMLHttpRequest inside a button onsubmit with a e.preventDefault()), causing the backend to write a JWT httponly cookie, and redirects back to the root (/), and a re-render of the entire SPA DOM with the cookie now stored in the browser.
Frontend needs to request protected data from backend via fetch/axios.
Before sending that request for data, frontend requests the CSRF token from the backend which is validated via JWT httponly cookie.
The actual request to the protected resource is made with both the httponly JWT cookie AND the CSRF Token sent as a header.
Potential flaws to this logic: Because retrieving the CSRF Token only requires the httponly JWT cookie to validate the session, this is potentially the weak link in protecting all calls. However, because the CSRF token is never stored, it adds an extra layer of security.
Alternative approaches:
Store CSRF Token in localStorage. I'm not sure, but I have a bad feeling about this approach since you can just do localStorage.getItem('csrf_token_name') in the browser console to retrieve the CSRF token.
Store CSRF token in JavaScript location. In React terms, either use a CSRFContext (via createContext()), or if you are a masochist, Redux. This approach is cleaner than retrieving the CSRF token before every protected API call. Also, although this is probably more hidden than localStorage, due to production minifying JavaScript logic as well as other pseudo-protection patterns that are afforded to modern compiled frontend code, you probably want to minimize storing anything in JavaScript if possible.
With both of these alternative solutions, you are still stuck with the problem of needing to retrieve the CSRF token from the backend via cookie session validation alone.

Security on $https requests

Good Morning,
I'm developing an app in ionic and there are some $http requests(angular) that send data to a server controller(yii 1) to save data on database. I finished my app, but it doesn't have security. I was wondering how to protect it, because right now anyone if look my $http request can know what parameters have to send, and kill my app.
What I should do to protect it? Maybe through tokens? I'm really lost with security methods.
Thank you so much,
Carles.
well When you research web application security you will come across Cross-Site Request Forgery (CSRF). This attack vector is taking advantage of cookies, but in a preventable way.
Most attacks focus on stealing your cookies because nearly every website uses cookies as a form of authentication. The setup is this: when a user logs into your server, you set a cookie in the browser. This cookie contains a unique ID which is a link to the user’s session information in your database. The browser supplies this cookie on future requests, and the server knows who you are.
On the surface this sounds not-so-bad, but here is the catch: the web browser can be tricked into making requests to your server, even if the end-user didn’t perform the action themselves.
Using POST Requests
It is sometimes thought that using proper form-based POST requests will mitigate this attack, but that is not true.
Using HTTP-Only or Secure cookies
While you definitely should use these flags on your session cookie, they don’t implicitly stop the attack: the browser still sends the cookies to your domain when a request is made to your domain. Your server does not know if this is a real user or an attack.
How To Prevent CSRF
You can achieve this by relying on a set of rules that browsers respect, called the Same-Origin Policy. This policy asserts that certain sensitive operations are performed by JavaScript code that is running on our website, not some other website.
Angular packages the CSRF token approach, making it simpler for us to implement. For every request that your Angular application makes of your server, the Angular $http service will do these things automatically:
Look for a cookie named XSRF-TOKEN on the current domain.
If that cookie is found, it reads the value and adds it to the request as the X-XSRF-TOKEN header.
Thus the client-side implementation is handled for you, automatically! But this does leave the server side pieces in your hands. You will need to do the following parts:
During login: create the CSRF token (with a random, un-guessable string), and associate it with the user session. You will need to send it on the login response as the XSRF-TOKEN cookie.
Assert that all incoming requests to your API have the X-XSRF-TOKEN header, and that the value of the header is the token that is associated with the user’s session.
That’s it! With a little bit of backend work, you now have a strategy which protects you from CSRF attacks.
you will find a working example of how to prevent CSRF attack here
and for theory of understanding CSRF attacks please follow this reference

AngularJS security tokens vs sessions

I'm now two weeks into learning and building an AngularJS+ PHPsystem and I'm still struggling with authentication. I've been reading a lot of posts about AngularJSand not one of them seem to consider the security aspect of authentication. I also had an interesting response when I asked about the security of AngularJS storages on another post, and got two great links to Stormpath's blogs which cover areas of security when dealing with tokens.
Most tutorials and examples about AngularJS seem to be taking a JWT approach and sending that token to your REST API via HTTP headers, but given that the token is stored in Javascript this can expose it to multiple attack types. One of them being MITM. To be secure against this type of attack the solution is to set a cookie with HttpOnly and Secure flags. Now the token gets passed on every request, it's not being stored by Javascript and it's secure. However, this raises the question at the point where you authenticate the user: How is this any different than using sessions when you're only dealing with HTTP requests originating from the same server?
When checking if a user has already logged in we usually check if a $_SESSION variable exists, let's say uid. Now on a token based approach we send the token in HTTP headers and read that token, then validate it and get user information. In AngularJSwe then get the successful response and return a promise.
Sessions have the advantage of being handled by the server. They create a session and they handle it's destruction automatically if it still lingers there. When dealing with a token based authentication you have to take care of it's expiration, refreshing and destruction with a scheduled script if the user has not destroyed it himself. This seems like too much work.
The idea of using tokens is to allow for a server to be completely stateless. The server just provides a login service, that upon successful login returns a temporary token, and it immediately forgets the token, it does not store it anywhere (database, memory).
Then the client sends the token at each subsequent request. The token has the property that it's self-validating: it includes the validity, the username and a cryptographic signature.
Such signature proves that the token is valid to the server, even if the server had thrown away the token completely.
This way the server does not have to take care of expiration/destruction of tokens: it can inspect incoming tokens and validate them inspecting only the token (thanks to the signature).
And this is the advantage of JSON Web Tokens: they allow for a completely stateless server that does not have to manage authentication token lifecycle.

Authenticate client-side app to REST API using CORS with local strategy

The Problem:
Serving a secure API to a client side app using only a local authentication strategy. The red arrows are part of the knowledge gap.
Context:
That is --- client.example.com is making a POST to api.example.com/login where on success client.example.com can gain access to a GET service like api.example.com/secret.
An idea!
Implimentation of OAuth 2.0 with hybrid grant type sitting in front of API.
Why hybrid?
It wouldn't be an Implicit Grant Flow aka Client-Side Web Applications Flow because there is no redirection to API server too grant access token. (i.e.) "Is it ok for so-and-so to access your data?"
It wouldn't be a Resource Owner Password Flow because a Client ID and Client Secret are passed along with the request so it's assumed the client app is server-side.
OK... so what about a little bit of both?
What if we used a CRSF token on page load of client-side app, and POST it with user credentials too OAuth 2.0 authentication endpoint to exchange for access token? You would authenticate each subsequent request with the access token and CRSF token after a successful login.
A good Node.js OAuth 2.0 library I found:
https://github.com/ammmir/node-oauth2-provider
Help Me!
I can not find a working example of an authentication measure that solves this problem! Point me in the right direction?
Ultimately, the goal here is too authenticate a client side app to a REST api using CORS with a local strategy --- i.e. username & password --- even if the convention above isn't possible.
To Accommodate Bounty:
This is a client side app, so let's stay trendy.
I'm looking for a working example using the Node.js OAuth 2.0 seed above for the API/Auth server and a front end framework like Angular.js or Backbone.js to make requests.
The example should match the context described above.
I'm working on an app with a pretty similar architecture though the services are .NET Web API rather than Node and we're using DotNetOpenAuth for the OAuth provider. Rather than the hybrid approach you're suggesting we're doing the following:
x.com serves up a login page
login page POSTs back credentials to x.com
server side logic at x.com combines client_id and client_secret with the credentials to submit a token request (resource owner password credentials grant that you've
mentioned above) receiving back both a temporary access token and a
refresh token
the refresh token is encrypted into a cookie issued by x.com
both the cookie (with encrypted refresh token) and the temporary access token are then sent to the browser
the client app (angular in my case) can now use the access token to hit api.x.com for services (It appears you're well aware of the limitations of CORS... we hacked a version of angular's $resource to facilitate this but it wasn't pretty since we wanted to use all HTTP verbs and support IE9)
when the access token expires, the client side app can request a new access token from x.com
server-side, x.com decrypts the cookie to get at the refresh token and issues another oauth call for a new access token
This is fairly high-level but hopefully gives you a sense for how to tackle your situation. In my case, and it appears in yours, we didn't want to use session state or a database to store the refresh token but obviously exposing that to the browser introduces security concerns so the encryption of the refresh token is important (among other security considerations) and the use of the cookie eliminates the need for session state or other persistent storage on x.com.
Not an answer running for the prize. Just my 2 cents :)
On my web server,
I do my authentication through a rest call with login/password with basic authentication over https. This call delivers a key to the client (a one page web app).
Then every subsequent REST call is signed with the key. The server checks that the signature is correct and everything still happen in https.
This mechanism is quite used I believe.
I don't see the issue with cross domain. I have a single source anf if I need something from another source, I'd use JSONP.
I use nginx as an https->http forwarder.
Not sure how it competes with an OAuth2 solution.
I've built this example using Node and PassportJS to show how to authenticate the users with Facebook or Local Strategy. Both sides are on different domains as you described and it requires CORS enabled.
GitHub: https://github.com/pablodenadai/Corsnection
Live demo: http://corsnection-client.herokuapp.com/
I can't promise that I have time to write working example but I can show you 2 paths :)
The biggest deal is CORS. After you solve that problem it is easy to use $http service. So, first and probably easiest may be to configure reverse proxy in x.com webserver which points to api.x.com. I wrote article here
Second approach is better, and created for exactly this purpose, to authorise specific domain to use your resource. It involves a bit of coding in api.x.com so you don't have to change anything in new web applications served in other domains. You simply need to authorise CORS requests in api.x.com service.
Create table in database where you can manage list of authorised domains
Add in that table record "x.com"
in api.x.com add request filter/interceptor what ever tech term you use for method which should be invoked after request is handled and add in response Access-Control-Allow-Origin: x.com if request comes from x.com (in other words check in request header refer value match to any value in table above and put that value in Access-Control-Allow-Origin response header).
That is all :) After this if you know how to use $http or jQuey.ajax you will be able to POST/PUT/DELETE/... any request to api.x.com from any authorised domain in just few minutes.
I very similar idea using vinilla js web app and cross domain authentication to GAE backend or OpenID connect.
The web app is run on CDN. When click login link, it goes to respective login server and redirect back to the web app (with XSRF security token and HTTPS only cookie). Login server accept cross domain request with credentials. XSRF token has to be set (in header) with every request. cookie is set by the browser. Since it is HTTP only cookie, JS cannot read it. The technique is very secure.
Once login, you can get secure assess from login server.
For detail description, you can find here and open source repo here.

How do you secure a RESTful API being consumed by a browser from CSRF attacks?

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?
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.
The answer depends on what you need to support. If we assume that you want to support a web-app which uses the REST service and use the same REST service for an API that is something different to a web-app that happens to be RESTFUL (you may decide that 'sessions' are for you! ).
For now (/me is rather tired) I think the way you have suggested using javascript with HTTP Basic Auth is a good start.
I have another potential solution, so I'm listing it as an answer. Please feel free to take it apart :)
auth.platform.com takes authentication and sets cookies. If auth.site.com is a CNAME for auth.platform.com, would the request to auth.site.com (ending up at auth.platform.com after resolution) be able to set a cookie for site.com? That way I could double-submit session cookies.
Of course, auth.platform.com will only set cookies for a few whitelisted domains.
EDIT: Except of course this won't work at all, because you'd have to use HTTPS to do the authentication securely, and HTTPS will see right through your trickery.
Designing your API as a true RESTful one, prevents the most common CSRF vectors:
avoiding cookies prevents them to be "stolen",
using GET for "safe" operations prevents img and iframes to trigger unsafe actions without user interaction.
Then you should implement CORS to let users' browsers block requests from origins you don't trust.

Categories

Resources