Stateless REST API with CSRF and XSS protection - rest

Is it possible to protect stateless REST API from both XSS and CSRF attacks?
Currently, I'm using JWT token stored in secure/httpOnly cookie for stateless authentication. This should protect the API from most common XSS attack: stealing cookies with XSS-injected JavaScript and sending them to the attacker.
However, this doesn't protect the API from CSRF attack, where attacker would trick the authenticated user to follow a link to the particular web API call to actuate adverse transaction on behalf of the victim. How could I protect the API from this attack without introducing server side state?
Also, is it true XSS vulnerability would inheritedly allow CSRF type attack in the following scenario: Injected JavaScript would retrieve CSRF token from the client side state, DOM or the browser storage and prepare a malicious ajax call to the server. Browser would still automatically include the httpOnly cookie for the same origin request. Is there a way to get protected from this other than protecting from the XSS vulnerability in the first place?

Stateless anti-forgery tokens
The first question is about preventing CSRF without server-side state. The current approach for stateless anti-forgery tokens is the double submit cookie pattern.
CSRF attacks depend on the browser automatically sending the API's own cookies back to it, regardless of where the request originated. The attacker doesn't have or need access to the cookie contents. They only need to trick the user into loading malicious HTML. The double cookie pattern adds a new requirement: the attacker must know and separately send the contents of the anti-forgery cookie.
There are ways for an attacker to overwrite the anti-forgery cookie with their own. So you may want to look at some additional security hardening for this approach. Especially HMAC signing the anti-forgery token to prevent token substitution. Using HSTS and the __Host cookie prefix to ensure transport security and proper cookie scope. And having the client provide the anti-forgery token in a custom header.
The custom header ensures the request must be sent from JS, since HTML-tag-based CSRF attacks cannot set custom headers. Being sent from JS also triggers additional protections. For cross-origin requests it triggers SOP on the browser and CORS validation on the server. The server can also do basic Origin validation.
Regarding the scope of CSRF attacks, here is a note from the OWASP CSRF link at the top.
Forcing the victim to retrieve data doesn’t benefit an attacker because the attacker doesn’t receive the response, the victim does. As such, CSRF attacks target state-changing requests.
XSS question
Yes, an XSS attack could happen that way. Once a malicious script is loaded into JS, it has free reign to use anything accessible to Javascript. Including app code/memory, browser storage, and cookies. Even though HttpOnly cookies are not readable, they can still be sent in requests. A non-targeted attack might look for key data in locations used by popular frameworks. And might try to probe for popular/discoverable API frameworks on the server. A targeted attack means the attacker studied your system and crafted a custom payload for it.
The primary vector for XSS is unsanitized external data (user input, database values, etc.). To prevent XSS, check out these guidelines from OWASP. Of note is that front-end frameworks like Angular, React, Svelte, Vue, and others have built-in XSS protection. Because they sanitize data before rendering it. Example: an attacker enters an HTML string into an input. When it is later displayed, these frameworks HTML-encode the string. So left and right angle brackets are replaced with < and > and so on. This causes the browser to evaluate the string as text instead of runnable HTML. But you can still mess it up in different ways if careless.
XSS attacks can also come from external resources. Maybe a compromised CDN or NPM script. If your deployed code takes dependencies on NPM libraries, pay extra attention to NPM audit warnings. One attack pattern is to attach a small loader (easier to go unnoticed), which will fetch attack payloads at runtime. A CSP policy can help prevent this. Specify an allowed list of resources -- your app assets and API URLs -- and block all others. CSP can also prevent data exfiltration.
For both CSRF and XSS
For attacks on your API, you can additionally employ server-side mitigations. IP-based throttling / temporary bans, unusual activity grey-listing and alerting, etc.
For especially sensitive operations, one strategy is to use an escalated authorization. This could take the form of requiring the user to re-authenticate or use a 2nd factor like a one-time password. This can block automated attacks since it requires active user intervention.
To mitigate attackers using existing escalated authorizations (which may also be in a cookie or app memory), they can have short expirations and be limited to specific operations. To go even further, the approved operations + relevant data can be signed. This will prevent malicious scripts from reusing an escalation to execute the same kind of operation with different data. Even further, these sensitive operations can be idempotent. So if an attacker does resend already-executed operations, they will not cause any additional changes in the system.
Zooming out
Try to engineer your systems to be as uninteresting as possible to hackers. Use trusted external services for sensitive needs like credit cards and credentials. Ideally (from a security perspective) the hacking risk would be reduced to just data vandalism/loss. So in the worst case that a breach happens, it would have little long-term impact. And it could be recovered with common practices, including routinely tested backups. And then further mitigations added for the specific attacks used.
Tread especially carefully with user-to-user interaction. Since this adds more targets and pivot points for hackers -- your user base. The most security-conscious eyes should be on these pieces. Not only for technical dangers like user-to-user XSS attacks, but also social ones. Human weaknesses are the most well-known and easiest to exploit.
Lastly, there is no sense investing in mitigations which have low risk/consequences in your situation. So do not take everything here as a requirements checklist. It's not a complete list anyway. Focus on your biggest risks first. Reevaluate periodically.

You can generate a token (e.g. a UUID) and put it into the jwt token as well as send it back to the client. Then the client sends it during every each request in a header. Then the server can compare the token in the header and the token in the jwt token to make sure the request comes from the client who was aunthenticated.

Related

How to securely implement authentication in Single Page Applications (SPAs) with a decoupled API

I have been researching how best to implement authentication in a React/Next.js application - specifically, how to store authentication tokens in a manner which is practical, while maintaining a needful emphasis on security. There is some existing debate about this topic on SO but as far as I can see, none offer concrete solutions.
Having spent much of yesterday and today trawling the internet for answers, I came across the following:
Local Storage API. I found that some basic guides suggest the use of localStorage (though many rightfully advise against it). I do not like this approach because data stored in localStorage could be accessed in the event of an XSS attack.
Web Workers. If the token is stored in a web worker, the user will not be logged in if a new tab is opened. This makes for a substandard and confusing user experience.
Closures. Same as Web Workers - there is no persistence.
HttpOnly Cookies. On the one hand, such cookies cannot be read by JavaScript so it's not prone to XSS. However, on the other hand, we now have to deal with CSRF, which is a new debate altogether: how does one implement CSRF tokens with an SPA + REST API?
While the use of HttpOnly cookies seems most favourable, implementing CSRF in an SPA seems unconventional and perhaps experimental, contravening the maxim of not "rolling your own" when it comes to security. I'd rather use a proven method if one exists.
With SPAs being all the rage nowadays, I am surprised that it's proving so difficult to find a consensus (or even better, a library) for such a prevalent scenario.
How is everyone else doing it?
Update: After some thought, I wonder if localStorage is really that bad if there is a strong CORS policy? If there happens to be an XSS vulnerability, couldn't the attacker start sending requests from within the browsing context anyway, negating any perceived benefit of using cookies and CSRF protection?
I'm happy that you're asking this question. There are recurring memes regarding oauth2 on the frontend that are really polluting the debate, and finding factual information is difficult.
First, regarding some excluded options which I suggest reconsidering: if you need the same authentication on multiple tabs, you can still use any option that would store tokens in a window scope, but individually manage tokens and get a new one on page refresh (silent refresh, thus standard prompt=none flow). This opens some options: service worker, web workers, closures... True, some of this isn't meant for that originally, but it solves the problem nicely. This also solves a bunch of race conditions about refresh tokens (they can only be used once, so having one for each tab solves a bunch of problems).
That being said, here are the options:
Local storage: in case of successful XSS attacks, tokens can be stolen. XSS=game over anyway IMO (no hacker will care about your token in such a case, it's not needed). It can also be mitigated by having short-lived tokens in comparison with the typicial hours/days validity of cookies. In any case, short-lived tokens are recommended.
Now, stolen tokens in case of XSS seem to be an important issue for some people, so let's look at the other options anyway.
Session storage: same downsides as local storage (XSS can lead to session leakage), introduces its own CSRF issues, but also solves some others (refresh...).
Web workers: this is actually a nice solution. In case of successful XSS in a random part of the application, it won't be able to steal tokens. In theory, if one could inject some script that would run at authentication (auth code or token exchange), it could be exploited too... but that's true for all flows, including cookies/sessions.
Closures: same as web workers. Less isolated (easier to replace by one that would steal your token).
Service worker: ideal solution in my opinion. Easy to implement (you can just intercept fetch requests and add your token in a few lines code). Can't be injected by XSS. You could even kind of argue that it's actually meant for that exact use case. It also solves the case of multiple applications on a page (they share 1 service worker which adds token when required), which none of the other options solves nicely. Only downside: browser can terminate it, you need to implement something to extend it's lifetime (but there is a documented, standard way).
HttpOnly Cookies: in short, you're then transitioning to a traditional server-side web application (at least for some parts), it's not an independent SPA with standard oidc or aouth2 anymore. It's a choice (it's not been mine for some years now), but it shouldn't be motivated by token storage, as there are options for that which are even secure and arguably better.
Conclusion: my recommendation is to just use local/session storage. Successful XSS will probably cost you your job or your customer anyway (hint: nobody is interested in your tokens when they can call the pay(5000000, lulzsecAccount) API).
If you're picky about token storage, service worker is the best choice IMO.
The solution is a HttpOnly SameSite Cookie.
In the question, you correctly note that HttpOnly protects from XSS. SameSite in turn protects from CSRF. Both options are supported by all modern browsers.
This is orders of magnitude simpler and safer than other solutions. It's easy to set up on the API and completely transparent to the SPA. Security is built-in.
Concrete solution:
The actual authentication can be done by your API or by an external provider that redirects to your API. Then, when logged-in, your API creates a JWT token and stores it in a HttpOnly SameSite Cookie. You can see this at work with NestJS at nestjs-starter as explained in: OAuth2 in NestJS for Social Login (Google, Facebook, Twitter, etc).
One limitation is that API and SPA have to be on the same domains.
For rather storing the token client-side, this other article is very comprehensive.
Your comparison is a bit confusing... When you talk about SPA authentication, you need a solution that alows you to store authentication tokens. The main purpose of Web Workers and Closures is to run code, not for to store authentication tokens. You can write it down as a hard coded variable but it's not its purpose and this is why a new tab won't identify it.
So we're left with Local Storage and Session Cookies.
Before SPA and client side rendering, we used to only have server side rendering and cookies. This is when HTTPOnly was invented to make it harder to steal session IDs and users' identities. When client side rendering was invented, stateless APIs were invented. These APIs do not use session IDs but Tokens instead, such as JWT. It means that the server do not save any information about the user in a session that is identified by a session ID. Instead, JWT tokens contains the signed information within the token itself, and the server do not remember anything and reauthenticate the user in each request. There is also a hybrid approach of tokens that are saved in NoSQL DBs in the server side such as Redis, to make the authentication process faster.
But the bottom line is that HTTPOnly cookies are used with stateful APIs that use sessions, and LocalStorage are used with stateless APIs that use tokens. You can also use it the other way around (cookies with tokens, LocalStorage with sessions) but its "less natural".
As for XSS, HttpOnly mechanism in cookies makes attackers' life a bit harder, but even if it is used, attackers can still do a lot of damage with phishing for example. So its not critical as this is just a compensating control and not a main mitigation in any way, so you can safely use LocalStorage. Furthermore, cookies are prone to CSRF attacks where LocalStorage does not.
So both options are valid.
You can read about it some more in here:
What is the difference between localStorage, sessionStorage, session and cookies?

Restrict REST API access to only registered site

I have an old question that maybe is asked here many times, but still we are in 2022 maybe there is some better approach to solve this problem.
I have an API that is used by some websites (clients) that is registered in this API, Those clients use this API to display the list of services for private and public users without any authentication.
When the client registered in the API a personal access token is given to send it for each request before using the API to limit abuse of this API.
The problem with an access token in public side, is anyone can intercept it and use it outside of the site generated for wish will cause a lot of cost.
Even if I reduce the lifetime of token to 10 min I still have the risque of abusing with it.
Is there any way to better secure this API other than authenticate the client for each request to get the fresh token with a lifetime in seconds?
To better illustrate the question above this is the use case:
The malicious user connects to the first registered site as the site list the services to the public users, the malicious user intercept the token in the header to the API
The malicious user come to his unregistered site and use the token
All this step can be automatised by the malicious user.
Edit:
I cannot change the client implementation or ask him for any change, I search something to reduce abuse not at 100 % but to make abuse a little complicated, like using origine domaine name, CORS, ...
The vast majority of access tokens used today are bearer tokens. This means that the resource server will accept the token if it is valid, and doesn't care about who has sent that token. You are right, this means that a malicious user can steal your token and use it to call your API. There are more ways for an attacker to steal access tokens, not only MITM attacks (which, as noted in the question's comments are pretty much mitigated by using TLS). You can have XSS attacks if the tokens are made available to a Javascript app, or Man-in-the-Browser attacks, where the token is stolen by a malicious code running in the browser.
These attacks are usually mitigated by using short-lived access tokens (even less than 10 min) and putting rate-limiting in place. This usually is enough to prevent abuse. You can always add additional layers of protection, e.g. drop a token if the caller's IP changes, etc.
If you need stronger protection, then you can have a look at sender-constrained access tokens. These are tokens, that can be used only by the legitimate client. Sending such a token requires the client to present additional proof of possession. This is then verified together with the token itself.
There are different possibilities for implementing sender-constrained tokens, e.g. Certificate-bound tokens or DPoP.

Do JWT refresh tokens provide more security? Where should one store them?

I am struggling to understand how JWT refresh tokens are safer than just using normal JWT access tokens with a long lifespan. I understand that by keeping the lifespan of JWT access tokens short, it limits the window of opportunity for attackers to abuse it. This assumes that the SSL layer of HTTPS has somehow been bypassed by the attacker in order to gain the JWT access token in the first place.
How do JWT refresh tokens solve this problem exactly? Once the access token expires, you would have to transfer the refresh token, which can also be hijacked if we assume that HTTPS is not secure enough. If the attacker gains control of the refresh token, then he now has access to a large supply of access tokens since refresh tokens typically have a long lifespan. By extension we could also say that the initial username/password authentication can be stolen if the HTTPS protocol is compromised.
Since the refresh token must be kept in the frontend (I am building a Angular/Spring boot application), we must take extra care that the refresh token can not be stolen client side as well. LocalStorage is clearly unsuitable to store the refresh token since it is not meant to be a secure storage. They are also unsuitable to be sent every request since they would be stolen together with the access token otherwise, which defeats the purpose of having short lifespan access tokens in the first place. Where should one store the refresh token?
If I wish to provide remember-me functionality at the loginpage, can I simply set the refresh token with an infinite lifespan?
I already went through several well written answers from the following links (and more):
What if JWT is stolen?
SPA best practices for authentication and session management
https://security.stackexchange.com/questions/119371/is-refreshing-an-expired-jwt-token-a-good-strategy
But I am left unsatisfied regarding these 3 questions.
I shall attempt to answer all the points in your question
Do not use JWT refresh tokens. Use Opaque refresh tokens. Typically, JWTs are to have very short life times. The reason for this is that revoking them may not be possible if you do not have blacklisting
You can store refresh tokens in HttpOnly, secure cookies. If you want to avoid CSRF and XSS, then you can split the access token and store half in cookies, and other half in localstorage
If you assume that https is compromised (which is actually possible), the best defence here is to have measures in place to detect stolen refresh tokens. You can do do by implementing rotating refresh tokens. This can also be used to implement remember me functionality quite easily and with the highest level of security.
In general, this topic is quite complex and it would be impossible for me to explain everything here. So here is a blog post I like that explain everything todo with session security. They also have an open source library called SuperTokens you can use that is by far the most secure implementation I have seen. They have it in various tech stacks, and can also implement one for your tech stack.
You've already received an answer and have selected it, but I thought I'd add another perspective.
I'll start by pointing out a bit of myth with one of your assumptions:
LocalStorage is clearly unsuitable to store the refresh token since it
is not meant to be a secure storage.
I'm sure some will disagree with me on this, but to me LocalStorage is just as secure as Cookie storage, if not more.
Cookies are susceptible to CSRF attacks, while LocalStorage not so much. And both LocalStorage and Cookies are susceptible to XSS attacks (even httpOnly cookies, since injected code could perform any operation withCredentials).
So from that perspective, Cookies offer a greater attack surface than LocalStorage.
Therefor I don't see any problem in storing access NOR refresh tokens in LocalStorage from purely a security perspective.
Beyond the security concern, you may need to store them in LocalStorage (or a non-Cookie store) depending on the platform(s) you deploy to, ex: some mobile frameworks don't support Cookies.
Conversely, if you plan to run a JS Web app that does server-side rendering, you may need Cookies since typically the server process will not have access to LocalStorage.
So the issue is not entirely one of security.
As for the main gist of your question, which I understood as:
If access tokens are susceptible to attacks, what makes refresh tokens helpful, as they too must be susceptible to the same attacks?
You're right. Both access tokens and refresh tokens can be compromised. The question is... what can your server do about it once it finds out?
The idea of access tokens and refresh tokens is that access tokens are short lived and refresh tokens are long lived.
Personally, I see little use in refresh tokens unless you're using JWTs as your access token, which is what you eluded to in your post.
As you probably know, JWTs are stateless (albeit you can implement white/black lists which would make them stateful, but that sort of defeats the purpose). Therefor there is nothing the server can do to disable a stateless JWT.
Due to this fact, some consider it risky to have long expirations on JWTs because they can't easily be disabled if compromised. I agree with this.
So to get the "best of both" worlds, one can use short-expiry JWTs (10 minutes or so) with long-expiry refresh tokens (many OAuth implementations never expire the refresh tokens).
This strategy gives your server some control back by allowing it to deny issuing new refresh tokens, thereby denying new access tokens, while also benefiting from some of the selling points of JWTs.

How to prevent session hijacking with JSON web tokens

I've been studying web tokens as part of an app I'm building, and I'm trying to figure out how you would prevent someone from stealing the token and using it wherever they want. Especially if the token gets transmitted via plaintext.
Also, how would I prevent replay attacks and cross site request forgery? Are there other mechanisms that take care of these problems?

GWT - Dealing with XSRF/CSRF

Am I correct, that if I pass a self-generated sessionID with every RPC request, and only check this sessionID instead of the one passed in the cookie header, the session can't be hijacked by malicious sites? I know that you should also send this sessionID in the cookie and then compare it with the one sent with every request to detect an XSRF attack but doing it my way should at least protect against XSRF attacks, doesn't it?
EDIT
I know that GWT 2.3 takes care of XSRF by providing XSRF Token Support. Sadly I'm stuck with GWT 2.2 and so have to deal with it by myself.
Yes, because the browser doesn't have enough information to convince your application that it has the right credentials. In a traditional XSRF attack the browser mechanism itself is being exploited and if it doesn't know how to send the extra information or what information to send then it just won't work.
However, with this approach, I would be aware that a malicious attacker could still compromise your self-generated sessionID and use it as soon as they figured out the mechanism.
See this wiki page on cryptographic nonce for more ideas. In using the nonce you're creating something that can only be used for that moment. Once the moment passes the data either becomes useless (in terms of a password salted with a time) or won't be accepted by the server. This is traditionally used to prevent replay attacks because, if you'll forgive me, the nonce has passed.
You might want to look at OWASP's CSRF Guard Project. They use a filter that checks every request to the server for the required CRSF Token. It's quite configurable - you can specify various aspects of your defense for ex:
URLs which don't require protection
entry point form which the CRSF Token
is to be generated (upon login)
the behaviour when a possible attack
is picked up (redirect, logging) etc
It's effectively a solution which requires no code change where the latest version also supports AJAX (RPC) calls to the server. So I believe it's definitely an option to try out (I'm currently POCing this solution for a fairly large GWT app).
Lastly I trust that you have already built your defenses against XSS. As XSRF defenses can be nullified if XSS is possible (not to mention that XSRF attacks are generally launched via XSS).