Mitigate CSRF attacks in React.js - javascript

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.

Related

NodeJS CSRF Protection with Express, GraphQL and CORS enabled

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.

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

Do cookies protect tokens against XSS attacks? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I'm building a JWT-based (JSON Web Token) authentication mechanism for an browser-based Javascript web app, working with a stateless server (no user-sessions!) and I want to know, once and for all, if using storing my JWT token in a cookie will protect my token from XSS attacks, or if there is no protection, so there's no real advantage over using browser local storage in my Javascript app.
I have seen this question asked and answered in SO and in many blogs, but I've never seen an answer that really satisfies me.
This question was originally held on the basis that it solicits opinion - and given my original wording, rightly so. So let me make it clear here and now that I don't want an opinion based on vague notions of developer laziness or such - that's what the ground rules are meant to eliminate. What I want is an evidence-backed Yes/No answer. Either:
"Yes, cookies can be protected from XSS and CSRF and here's how" or
"No, by protecting your cookies from CSRF, you always open them up to the same kind of XSS attack that made cookies a good idea in the first place"
So I'm going to restate the question, with some simplifying ground-rules, and point out the holes in advance, so that you, the experts, can set me straight.
Ground rules
Your app is a javascript browser app - it might be in AngularJS, but it might be custom-built. It communicates with the server via REST calls. Let's say, jQuery $ajax calls.
The server is stateless: there is no session management.
The app users JWTs as the main authentication token ('access token' in OAuth2 parlance) and validates them on the server using a secret signing key
Ignore other important advantages of cookies: browser management, less chance of coding poorly, etc. For this battle, I want to consider the absolute security, and assume we can competently code either mechanism.
Ignore other disadvantages of cookies, such as non-browser apps, etc. For this battle we are only concerned with a browser-based javascript app.
It doesn't matter whether you use a header or a request body to transmit tokens in the non-cookie approach; nor does it matter if you're using local storage vs session storage - ignore any security differences there. And yes I know that technically Cookies use headers, ignore that.
In a nutshell, we're only interested in comparing browser-handles-tokens vs your-javascript-handles-tokens and the comparative XSS and CSRF security risks.
The contestants
In the red corner, Auth0: Local Storage beats Cookies, because XSS is easier to fix than CSRF
In the blue corner, Stormpath: Cookies beats headers, because actually CSRF is easier to fix than XSS.
(excerpts of both arguments in detail below)
Weapons of choice
XSS and CSRF (we'll use CSRF and XSRF interchangeably: the C seems to be more popular in documentation, the X in code)
Here's my super-simplified summary of the attack types:
Let's assume your stateless, JWT-authenticated, javascript browser app is for online banking and the attacker, "Evil Corp", wants to submit an AJAX REST call that transfers funds to their account by impersonating your users.
XSS (Cross-site scripting)
(As Stormpath points out, there are many attack vectors - I'll pick one)
Evil Corp buys the github account rights for the nifty text field widget you use for password entry. They know your bank site uses it, so they update it to submit AJAX requests to transfer funds to their account when you type in your passord and hit enter. Your build system foolishly pulls the update and puts in production.
CSRF (Cross-Site Request Forgery)
Evil Corp knows your bank site uses JWTs in cookies to authenticate transactions, so they write a web app that submits AJAX requests to transfer funds to their account. They host this on their own evil.com site, and lure you there with an email (phishing) or some other way, when you happen to be logged into your bank site in another tab. The browser submits the request from evil.com, but attaches your JWT becaues it's going to the correct site: the bank.
Standard defences
The defence against XSS is to be very careful about the code in your site so that you never let the browser process something the user types in without sanitizing it (removing javascript and html) and that all the 3rd party libraries (Evil's text field widget) are vetted before being used. As Stormpath rightly points out, this is hard, bordering on impossible.
The defence against CSRF is to use a form of double-submit-cookie. This means our server creates a token (securely random string) and sends it to our Javascript browser app in a readable cookie (call it "XSRF-TOKEN" by convention), and our Javascript sends it back in a header or body with every request.
Actually, double-sumbit-cookie's are only one defence agasint CSRF, but some others require stateful server sessions and no other (I think!) offers any better protection. The statelessness can be achieved by also putting the token in the JWT and comparing it on the server side with the one that comes in the header or body.
But the real CSRF-busting quality of this defence, is that same-origin-policy mean that only the javascript that our app loaded from our domain can read that cookie. So even if the javascript on evilcorp.com can send our cookies with its requests, it can't embed our XSRF-TOKEN because it can't read it in the first place.
To really simplify CSRF:
CSRF attacks work because a browser attaching a cookie depends only on the destination of a request.
CSRF defences work because Javascript access to a cookie depends on the origin of the Javascript.
Auth0's argument
It's easier to deal with XSS than XSRF Cookies have this feature that
allows setting an HttpOnly flag from server side so they can only be
accessed on the server and not from JavaScript. This is useful because
it protects the content of that cookie to be accessed by injected
client-side code (XSS). Since tokens are stored in local/session
storage or a client side cookie, they are open to an XSS attack
getting the attacker access to the token. This is a valid concern, and
for that reason you should keep your tokens expiration low.
But if you
think about the attack surface on cookies, one of the main ones is
XSRF. The reality is that XSRF is one of the most misunderstood
attacks, and the average developer, might not even understand the
risk, so lots of applications lack anti-XSRF mechanism. However,
everybody understands what injection is. Put simply, if you allow
input on your website and then render that without escaping it, you
are open to XSS. So based on our experience, it is easier to protect
against XSS than protecting against XSRF. Adding to that, anti-XSRF is
not built-in on every web framework. XSS on the other hand is easy to
prevent by using the escape syntax available by default on most
template engines.
https://auth0.com/blog/2014/01/27/ten-things-you-should-know-about-tokens-and-cookies#xss-xsrf
Stormpath's argument
Stormpath recommends that you store your JWT in cookies for web
applications, because of the additional security they provide, and the
simplicity of protecting against CSRF with modern web frameworks.
HTML5 Web Storage is vulnerable to XSS, has a larger attack surface
area, and can impact all application users on a successful attack.
https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage/
Also:
I see a lot of discussions where cookies are pitted against access
tokens. While we’ve all been burned by systems that store a session ID
in a cookie, and that cookie is not secured and thus gets stolen. That
sucks, but its not a reason to use tokens. Its a reason to avoid
non-secure, non-https cookies.
https://stormpath.com/blog/token-auth-spa/
My take
Stormpath's argument in favour of cookies is pretty convincing, but there's a hole in it that I don't see them addressing clearly:
The double-submit CSRF defence relies on the fact that my CSRF attacker cannot access my cookie: the one with the XSRF-TOKEN in it. But isn't that cookie just as vulnerable in an XSS attack as local storage?
An XSS exploit can run javascript on my domain, so it can read the same cookies my javascript can. (The browser doesn't know that it isn't my Javascript)
To look at it from the other side: local storage is protected by the same-origin-policy just as much as a readable cookie. If I'm using the Auth0 approach, and an XSS attacker knows how to find my JWT in local storage and use it. Can't that same attacker use that same XSS script to grab my XSRF-TOKEN cookie and use that?
Both attacks require them to read and understand my javascript app, but that's out there in their browser.
So what's the difference? Is one really more secure than another, and why?
Abandon all hope unless you can secure against XSS!
Or
Choose the approach that suits you based on other criteria because both are equally secure, equally insecure.
If you use cookies, you should definitely use the double-submit-cookie defence, or something similar, because it does protect you against CSRF in the absence of XSS. That is, if you don't do this, you're definitely open to CSRF attacks - from other domains - that don't even require XSS exploits to work.
But either way, your source code is publicly available (JavaScript in your browser) so for a motivated hacker, there is no significant difference in effort between finding which token to pull from local storage and reading your XSRF-TOKEN cookie. If Evil Corp can get some JavaScript running in your domain - that's XSS - then you're hosed.
Non-security-related criteria you might want to consider for your choice:
Cookies are convenient because you don't have to write JavaScript code to manage the token - only the XSRF.
Redirection becomes a little more automatic too, if you want to use it.
Local storage is easier to adapt to non-browser apps - from the server perspective that is, because if you write say, an Android app in Java that doesn't want to deal with cookies, your server doesn't need to make any distinction between in and the browser, since it's not using cookies.
Anyway, make up your own mind, but be careful about the JavaScript you write and the 3rd party JavaScript you use!

jwt authentication: cookie vs header

There are a lot of articles around discussing what is the best place to store JWT on the clientside. In short, they're all about -
Http-only secure cookie - no XSS, but vulnarable to XSRF
Header (saved in local storage or DOM) - no XSRF, but vulnarable to XSS
I think I come up with an extremely savvy solution to this, but, since I'm complete noob in security I'm not sure if it's really savvy or stupid.
So, what if to split JWT and save part of it in the cookie and another part in the header? Would it be unbreakable?
This should also solve 'logout' problem - deleting header portion would make browser incapable of logging in.
The JWT needs to remain together, otherwise the signature validation won't work.
Protecting against XSRF is pretty easy, you just need another cookie.
Never use local storage for storing authentication information, it doesn't follow the same domain and origin rules as cookies. Read more here:
https://www.owasp.org/index.php/HTML5_Security_Cheat_Sheet#Storage_APIs
Disclaimer: I work at Stormpath, we have a hosted user management solution and we spend a lot of time on security. I've written two blog posts where I discuss JWTs and front-end auth:
Token Based Authentication for Single Page Apps (SPAs)
https://stormpath.com/blog/build-secure-user-interfaces-using-jwts/

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