Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

security: pulling tokens from localStorage is not secure #149

Open
kevinburke opened this issue Oct 25, 2018 · 13 comments
Open

security: pulling tokens from localStorage is not secure #149

kevinburke opened this issue Oct 25, 2018 · 13 comments

Comments

@kevinburke
Copy link

The documentation for header authentication says:

Another common way to identify yourself when using HTTP is to send along an authorization header. It’s easy to add an authorization header to every HTTP request by chaining together Apollo Links. In this example, we’ll pull the login token from localStorage every time a request is sent:

This is not secure. Any other Javascript running on the page (not to mention attackers using a 'learn who is visiting your profile page by pasting this code into the console!' attack) can read a token out of localStorage and do whatever it wants with it, and as far as I can tell, it's pretty common for people to run a lot of third party Javascript on their website.

The documentation recommending localStorage should at the very least mention this.

@lifeiscontent
Copy link

@kevinburke what is an alternative solution to localStorage?

@kevinburke
Copy link
Author

kevinburke commented Oct 25, 2018

Use cookies with the HttpOnly, Secure and SameSite flags set, which store authentication information in the browser in a way that isn't accessible to other Javascript running on the page, and protects users against CSRF.

https://auth0.com/docs/security/store-tokens#don-t-store-tokens-in-local-storage
https://www.rdegges.com/2018/please-stop-using-local-storage/
https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage (this blog post predates the SameSite cookie flag)

@pyramation
Copy link

I just want to paste a quote from a comment that I found in this link https://dev.to/rdegges/please-stop-using-local-storage-1i04

‘’’
I would argue that localStorage is as secure as cookies (including httpOnly cookies).

localStorage uses essentially the same security policy as cookies; one of its core principles is that a domain cannot access localStorage data that was created under a different domain so there is no chance that a website could steal data from a different website.

Also, httpOnly cookies do not make your site any less vulnerable to XSS attacks; if the attacker manages to inject a malicious script into your front end, then they can use that script to make HTTP requests to your server (directly from the victim's browser) and your precious httpOnly cookie (containing the user's valid session ID) will be attached to every request so the server will service them without suspecting anything.

The only real difference is that if the token (e.g. JWT) is in localStorage then the attacker can steal the token to use later (same goes for regular non-httpOnly cookies BTW)... Which is hardly a convenience because it's more advantageous for the attacker to carry-out the attack in-place from the victim's browser rather than from the attacker's own machine (thus allowing their IP to be traced directly).

Also, with JWTs, it's good practice to set short expiry dates. If you're using WebSockets you can even issue JWTs with 10 minute expiry (for example) and re-issue a new one automatically every 8 minutes if the user is still connected and logged in; then when the user logs out or becomes disconnected; their last issued JWT will become invalid in only 10 minutes (at which point it becomes completely useless to an attacker).
‘’’

@kevinburke
Copy link
Author

kevinburke commented Oct 26, 2018

If you run third party Javascript on your site or XSS and it gets popped your localStorage tokens are toast. httpOnly cookies do make your site safer; they prevent exfiltrating the token. Having an valid token or session identifier is more valuable than being able to make requests using that token, it's a superset of the behavior available when you have access to a user browser.. IP addresses are cheap.

You can reissue a valid cookie every ten minutes, or on every single request if you want, I don't understand why that is an argument in favor of using local storage.

Don't listen to me, if you want, listen to OWASP: https://www.owasp.org/index.php/HTML5_Security_Cheat_Sheet#Local_Storage

@jonathaningram
Copy link

This is an important issue and I think the example in the Apollo docs might need to be removed.

References (some repeated above):

I've also made a simplified example application that shows this issue in real life:

@hwillson hwillson transferred this issue from apollographql/apollo-client Jul 9, 2019
@hwillson hwillson added the project-apollo-client (legacy) LEGACY TAG DO NOT USE label Jul 9, 2019
@seivan
Copy link

seivan commented Feb 22, 2020

@kevinburke Sorry to wake up an old issue, but why would a successful XSS attack care about localStorage and your access token? I am looking to be corrected here, so please go ahead and pick holes:

I mean an XSS attack at that point is game over, the environment has already been compromised.
Localstorage is the least of your concerns.

What are you actually protecting when XSS compromised, the token? It's irrelevant. You just lost everything else that actually matters: identifying the user of said requests.

SameSite is still in flux, not implemented/buggy on previous Safari versions for instance.
e.g Mojave and older iOS versions.

cookies (even httpOnly ) add another attack vector with CSFR attacks which in my opinion is a larger surface given how messy browsers are and how many rules there are at play.

You can mitigate CSFR attacks by using a CSFR token stored in localstorage combined with the httpOnly cookie.

But you're just solving a problem you introduced by using cookies to begin with

What I am trying to get at, is that by using cookies you're introducing a problem and then spend time solving it with CSFR tokens. You took time and resources to mitigate a problem you introduced when that should have been spent fixing glaring XSS issues that can happen and hardening your CSP policies.

And you're going to have to actually do that, unless you got a controlled environment and not dealing with various browsers in the wild, even from the same vendor.
In other words, actually using Cookies add additional work and concerns.
You can't rely on SameSite as of today (2020), you're expected to add workarounds to handle that.

it's pretty common for people to run a lot of third party Javascript on their website.

When XSS attacks happen, cookies won't protect against:

  • Keylogger to take the password & email on login.
  • Pop up a modal asking for password again during a session, not unheard of as even Github does this.
  • Change the existing password.
  • Can change email to reset the password.

To be clear, I am not making a distinction between a targeted attack or a generic attack that e.g just dumps the content of Localstorage, because both approaches can be used to force attackers to investigate your environment beforehand, so the distinction is moot.

Application dependent attacks:

  • Inviting the attacker to the organization/team/project/group/channel
  • Making the attacker admin on your resources

Sure you can add password requirements, 2FA and etc. But that's not related. You just compromised your users email & password and cookies didn't help at all except open up other potential vulnerabilities.

XSS is game over

I am wondering if I am wrong thinking like this or if nonsense like https://dev.to/rdegges/please-stop-using-local-storage-1i04 needs to die.

TL;DR
Do no harm. Cookies add harm that you have to mitigate.
Cookies might protect attacks leaking access tokens but why would an attacker care about the access token when they can run any code they wish?

@kevinburke
Copy link
Author

That response doesn’t make sense, sorry.

@seivan
Copy link

seivan commented Feb 22, 2020

Lol, fine.

@vans163
Copy link

vans163 commented Feb 25, 2020

That response doesn’t make sense, sorry.

If a malicious js script runs on the client side, (extension, sideloaded or copy+pasted), the attacker can just spoof requests to the endpoint and the cookie will be passed along (as requests coming from same domain). The Secure+HttpOnly cookie vs localStorage is only protecting the auth from being lifted, but a lifted auth is useless in most cases anyways as most systems (worth stealing) check ip + other fingerprints.

Example based on @seivan the malicious javascript calls the endpoint to trigger a password reset, (the cookie is already present since its called from the same domain), the user gets a password reset email. The user follows the password reset email, the malicious javascript is loaded on the domain (say the tracking library, PandaTracker, this app uses to track the amount of baby Panda's using the app and gather metrics, got compromised). The user now enters their new password, the malicious JS (having full access to page, pulls the password out the field) sends this password off to the attacker, CORS does not stop writes.

Or as @seivan also said, you can just pop up a modal saying, please reenter your password, most users will bite and give up their password.

There is a simple 6 step process to having a safe web app.

  • Audit every single piece of 3rd party javascript you include.
  • If you cannot audit it, do not assume it is safe, do not include it.
  • Audit every single node.js/other package you include in your app + hash every file.
  • If you cannot audit the node.js dep, do not include it.
  • If a hash changes, dont include it, the node repo might of gotten compromised.
  • sha512 every 3rd party audited dep you include in your app. The browser wont load it if it changed.

Bonus: Feel a little safer.

@Jonatthu
Copy link

Jonatthu commented Aug 3, 2020

That response doesn’t make sense, sorry.

@kevinburke Please explain why @seivan answer did not make sense. I short answer like this will lead to think that your previous arguments are actually irrelevant now. Also @seivan set effort and time to come with this detailed answer. Please answer to the parts that do not make sense in order to help everyone to understand as well.

We all trying to figure out this war of local storage vs cookies in order to secure our apps!

@pplonski
Copy link

@Jonatthu I spend a whole day reading about this! This is how if found this issue. I will agree with @seivan that

XSS is game over

Storing token in httpOnly cookie may raise the bar of safety but after XSS there will be no rescue.

The safest way is to keep the token in memory and ask the user to login after each page refresh. If you want to have a better UX, then just keep the token in localStorage and make your best to prevent XSS.

@RobertTownley
Copy link

RobertTownley commented Jun 4, 2021

One example of a threat vector that HTTP-Only cookies solves compared to XSS is an NPM supply chain attack.

A malicious dependency could exfiltrate and POST the entire contents of localStorage for examination later. That's a bigger threat than a malicious dependency publishing a targeted attack on your site (eg hitting the /users/me endpoint to see if that exists)

Agree that XSS attacks are to be avoided at all cost. But using a cookie can mitigate their impact while preserving the positive UX of persistent logins.

@seivan
Copy link

seivan commented Jun 4, 2021

@RobertTownley

One example of a threat vector that HTTP-Only cookies solves compared to XSS is an NPM supply chain attack.

A malicious dependency could exfiltrate and POST the entire contents of localStorage for examination later. That's a bigger threat than a malicious dependency publishing a targeted attack on your site (eg hitting the /users/me endpoint to see if that exists)

You still got malicious code running.
You still allow malicious code to make external requests and phone home.

Can't this be prevented with CSP to begin with?

Agree that XSS attacks are to be avoided at all cost. But using a cookie can mitigate their impact while preserving the positive UX of persistent logins.

Again, this is just security by obfuscation with extra steps.

It it's absolutely(!) true that the surface is much smaller, and easier to just dump local storage.

In a generic attack where the attack just dumps local storage to a third party service is not protected by a cookie, it's protected by the concept of the malicious code being generic.
An obfuscated scrambled token on the client before storing in local storage is just as "protected".

But then you ask, can't attacker look at your client side code and figure it out?
Sure but if they're spending that time, and can run code on your client, then you've just lost the password, and not the token. You're not mitigating anything by allowing the attackers code to run and phone home.

To be clear, I am not making a distinction between a targeted attack or a generic attack that e.g just dumps the content of Localstorage, because both approaches can be used to force attackers to investigate your environment beforehand, so the distinction is moot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests