How to prevent session hijacking with JSON web tokens - jwt

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?

Related

oAuth Silent authentication vs refresh token and http only cookie

Somehow implementing stateless authentication always brings me headaches.
This time it concerns silent auth vs refresh tokens.
Using refresh tokens seems discouraged, however there are certain arguments I don't really get.
If you use an http only cookie to store your refresh token, what exactly is the danger?
Attackers cannot get access the cookie with Javascript and if you use SSL (which you should), I really don't understand the problem.
The resources I read always say "you should not store sensitive data in the client". Seems like an automatic, but that is implicitly impossible if you want to eliminate the need for server session state. Neither do I really understand why, since no resource ever explains how it would be cracked (and I really wonder if anybody really knows).
The reason why I have this question is because using a refresh token offers me more than just authentication.
If a user for example loses his / her device, removing the refresh token will just invalidate all access tokens over all devices (not only browser), which seems like something a user wants to do.
After all, it makes sense that when you lose a device, you need to take action to protect your data.
So the argument "if the attacker gets access to the refresh token, he can infinitely refresh your token" sounds like another argument I don't get. The attacker should not get the refresh token. How would he ever get it? It's the same as saying "if the attacker gets hold of the code of your bank card, he has infinite access to you money". Well if you lose your bank card, you call card stop; likewise if you lose your refresh token, you would delete it to invalidate all access tokens. So how is this an argument?
Can you clarify why I cannot just store my refresh token in an http-only cookie, and how a silent authentication flow improves on this?
Edit:
Note that I read a few other articles that advise to store jwt in the browser by sending the encrypted jwt signature in an http-only cookie. These articles received a lot of upvotes, so that is suddenly okay. It makes zero sense to me.
Edit on comment:
The architecture is very simple:
React / Redux SPA with REST api in the backend
Need for social login through Google, LinkedIn, Github
Need to refresh the token without needed user interaction
Access my own api resources (preferably with jwt)
Ability to revoke refresh token
I don't know why it seems complex (lol).
Refresh tokens are widely used in:
Server side web apps, where they are stored in an HTTP only cookie, as you suggest
Desktop and mobile apps, where they can also be stored in OS secure storage
Refresh tokens should not be infinitely renewable and often represent the user session time - eg:
Refresh token / User session lifetime = 12 hours
Access token / API message credential lifetime = 60 mins
The concern for SPAs in the above article is that there is no real secure storage in the browser - though you are not intending to use browser storage - so no problems there.
One risk is that users can maybe get the secure cookie and replay it to an API via browser developer tools:
To mitigate this it is of course important to ensure that APIs have well engineered authorization - and that what the user can do with a token matches what they can do in the UI.
Another risk is CSRF where a malicious app in another browser tab sends the same cookie to your back end. So you'll need to protect against this.
Note that SPAs have their own token renewal solution based on Authorization Server cookies - I would prefer that option if using an SPA, rather than issuing your own cookie.

How to safely & properly storing JWT token in Electron App

I'm building an electron desktop app, and in the app it will call the remote API with JWT token. However, where should I persist this JWT token safely without the threats like XSS, CSRF, man in the middle attack etc... and cannot be accessible by other applications
I've tried using node-keytar package, which uses an encryption key derived from the user’s login.
But according to this issue(https://github.com/atom/node-keytar/issues/88), the credential(JWT in our scenario) can still be compromised if the user's environment got a malware installed.
The code of node-keytar is fairly easy, here's the add secret
keytar.addPassword('KeytarTest', 'AccountName', 'secret');
and here's the get secret
const secret = keytar.getPassword('KeytarTest', 'AccountName');
console.log(secret); // "supersecret"
I'm thinking about just storing JWT into the memory might be the safest way, but will require user to re-login and get JWT token everytime they reopen the electron desktop app.
Any suggestions or thoughts are welcomed. Thanks!
In many use cases involving JWT, you would not need to necessarily do any additional encryption/obfuscation of the JWT before you send it to the API, because:
When you send the JWT to the API, you would be doing so via SSL or HTTPS, which encrypts the entire payload. This in theory would eliminate most chances of man-in-the-middle attacks.
Even if someone managed to sniff your JWT token, they would lack the server's key which is required to unlock it. Also, even if they managed to unlock the JWT, it would be almost impossible to alter its contents without also altering the checksum, which is contained within the JWT itself. This eliminates the chance of injection attacks by inserting something into the JWT.
So in general, the JWT pattern is a way of pushing server side session state outside of the application. And it does it in such a way that this state is protected from tampering on the outside. If it were possible to easily tamper with a JWT on the outside, the entire pattern would fall apart.

Stateless REST API with CSRF and XSS protection

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.

Doubt on prevention of CSRF

I had one doubt about CSRF prevention. A lot of sites say that CSRF can be prevented by using 'tokens' which are randomly generated per session.
Now my doubt is,
suppose i have a function like :
$.post("abcd.php",{'fbuid':userid,'code':'<?php echo md5($_SESSION['randcode']); ?>'}
now this md5 hash would obviously be visible to any hacker through the source code.He could simply open this page, generate a token, and keep the page open, so that the session doesn't get destroyed, and useanother tab or anything else , to start hacking,
No ?
Or is my idea of tokens incorrect ?
Thanks for your help :D
I think you are misunderstanding what needs to be done. To protect against CSRF you need to create a token and save it for that session. Then you need to append all your submits and AJAX calls with that token.
For another person to send you to a page on your website they would need to have access to the request with in the same session. It is true that one could parse the HTML and look for the token. But when they try to request a http call on your website they will have a new session created. The new session will have a new token which will not match the token that was passed.
Next you will ask what if you can copy the cookies and the session id as a result. This is not something that is protected. I can simply sit anybody's computer and copy all their cookies and I will then be logged in as them.
As kapep points out, you are confusing the two seperate issues of input validation and cross-site form posting. You must validate your inputs anyway, so the case of your malicious attacker using his own session token is already handled if you have sound input validation. CSRF protection is not there to protect the data, it is simply to ensure that only forms from your own application can post data back to that application. the CSRF protection is simply stopping other people being able to post data directly into your app from forms they put up on their own site.
One specific point to be aware of is that the token is visible to any javascript running on your page, so as soon as you have a cross-site scripting (XSS) vulnerability, your CSRF protection is defeated.
See Cross-site scripting and the prevention cheat sheet
You should use a per request token.
Generate a token and store it in the session.
Pass the token to the client.
Execute actions.
Destroy the token.
The token is safer and cannot be used more than one time.
I would define a stolen token as a token that is used by someone else, and not the one you have send the token to. If you send someone a token he can't really steal it from himself.
If you are concerned that a user can run a malicious script with his own token, your design seems to be broken. You can't prevent a user from sending data that you didn't indented to receive. It's your job to validate any data, the session token is just there to identify multiple requests by the same client.
It could be a security issue if you send that token over unsecured http. Then it could easily be stolen by monitoring the clients network.

Way to maintain a session in a REST application

We have a REST application that is utilized mostly by applications that dont need to maintain their state, so till date we have been quiet "RESTFUL" without maintaining a state. We use the Private/Public (similar to Amazon) for authentication.Currently the client passes the credentials for every request
Now we have a new requirement where we have to maintain the state (or conversation).The client can be a Rich application or a hand held device .I am trying to comeup with the best way to implement the state .Should we pass on a session Id and maintain that ID ..is that the best and the only solution ?
Passing on a session ID is not the only way and not the best way to maintain conversational state. The best way, if you have a RIA is to maintain the state on the client itself, where it belongs, as some of the comments suggest. This means still sending the credentials every request.
Re-authentication on every request is the only way, and if you feel that there's a performance hit on the server, the server can (as suggested) cache the result of an authentication request for a period of time. Digest authentication could help avoid caching responses by cryptograpically signing the tokens going over the wire.
If that's not good enough you could use something akin to Google ClientLogin, and giving the client an encrypted token that can be verified without needing to ask an authorization, and without passing the user's real credentials over the wire. Google themselves this by doing the login over https, and then using the generated tokens over http. It's open for replay attacks for the lifetime of the token.