It's not about cookies or localStorage, it's about how you handle the validation and protecting methods IN YOUR BACKEND SERVER. When using cookies, there's still XSS, man-in-middle, you can't stop them, just slow them down. The more appropriate way to protect your user is stop using stupid packages from npm and try to use an access token with a refresh token, limit the number of refresh tokens per user, save the user's IP, browser information, and try to use the RTR (refresh token rotation) method.
Saying that a httponly cookie is safe and cant be accessed is extremely misleading and dangerous. They can be accessed by XSS and XHR attacks. Setting a cookie to httponly is one part of your multi-layer defense, on its own its not enough, but combined with other layers it helps make your service more secure
Yes but it's easier to mitigate against XSS attacks as you can restrict the cookie through SameSite policy, unlike any JavaScript-accessible storage medium.
I agree; more layers are better, of course. I'm not sure about XSS and XHR attacks being able to access httpOnly cookies, though. I thought the httpOnly attribute was specifically designed to offer protection against XSS/XHR attacks. Can you expand or point to an article/page/docs demonstrating this?
The httpOnly attribute of a cookie is exactly there to prevent XSS attacks, so you can't access it using that one. I haven't seen XHR attacks and looking online right now, I don't seem to find a way to access an httpOnly cookie as well.
@@cdruc The problem is not with XSS specifically (accessing the information directly through JS) but that a script can make a fetch from the website's origin and include the credentials (i.e. HttpOnly cookies). If SameSite is not set to either Lax or Strict, it will include those cookies in the XHR request. By default modern browsers will set the SameSite to Lax if it is not explicitly set to None by the website issuing the cookie. So unless you go out of your way to disable it, your users will be safe (also safe against CSRF attacks).
Thank you for this video. I saw these videos (same topic) so often and never understood why it is so important, but your example with the package is so good.
There is another interesting thing that discord does, they store the token in localStorage but: 1. When they load the page the key is deleted from localStorage and loaded in memory 2. When a user leaves the website or closes chrome or the discord app they store it again in localStorage so they can get it on the next load. This mostly works for web applications that don't require opening in new tabs tho, because opening new tabs of your website means it doesn't know the current auth token as it is not in local storage anymore until you close the tab that currently has access to it.
yeah I don't agree. if your web app is compromised by a rogue package, it is game over. even if you have a http only cookie, yes, they might not be able to steal the token exactly, but they can still make requests to your backend through the client browser. the cookie will be sent automatically, and they will do what they want to do regardless. they just won't have the token at hand, they'll be able to use it. this is common advice but security theater. with cookies you will have to start to worry about CSRF etc as well. so no method is without its drawbacks. you need to have a threat model. if your threat model is "infected scripts in my site" then neither cookies, nor local storage will save you.
An infected script could even load additional javascript that exploits the requests on your page. Do requests to your server and then send the data to another server. If you have untrusted 3rd party scripts on your page nothing will save you. If you do not sanitize user input also anything can happen.
I remember being onboarded on a government project that stored JWTs in localstorage which required logging in using your national ID. It was a hell of a shock it got through code reviews and the eyes of every dev who went through it.
Well, unless you disallow access to any other domain with using Content Security Policy (CSP): For example `Content-Security-Policy: default-src 'self'; connect-src 'self'` there is no way to send data to another domain
CSP does not govern browser extensions, nor does it protect from relay attacks. Though, the latter requires you to have a relay vulnerability in your server implementation.
@@_f0x604 No, CORS is aimed at stoping Cross-site Request Forgery, tricking the browser into sending the cookies to perform actions on Website B while the user browses Website A. CSP prevents exactly what's described in the video, exfiltrating data from a site with malicious scripts that are injected into the site (XSS).
@@trapfethen yes, but browser extensions can inject any code and just read you password as you type. When you have a node app, you also have to make sure that the server cannot send data to a malicious sie.
While not entirely wrong, this video is definitely misleading... By this logic, storing tokens anywhere on the client device is insecure. Now there are definitely limits on both ends, but they really shouldn't be defined by the libraries which are under your control. If this is your concern then I have bad news for you: it's probably not much harder to just steal the credentials while logging in, which is obviously even worse. And that's just the tip of the iceberg probably... The real solution is to vet your libraries properly, which is hard yes, but using a compromised library is dangerous no matter where you store your tokens... Also if you say you should use tokens for mobile apps, then what about compromised libraries in the mobile app? And what about the server, how are you gonna hide things from a malicious library there? All that doesn't mean you shouldn't use cookies for authentication if you can though. There are other benefits to that approach - it is basically what they were made for afterall. It's just that they don't really solve the problem described here...
I see so many articles, like "authentication in React" that says to store it in localStorage. Thanks for making this video, I hope it will reach as many people as possible.
Well done video! The flow on information was really easy for me to follow, with examples and proofs. I will be making this change, and letting others know too, thanks.
hash your Auth token with a browser fingerprint and store it, that way you can't spoof it on other computers, there are some libraries that help you do it but its definitely possible with a custom made function.
Browser fingerprinting is not reliable and there are a lot of collisions (especially in browsers like Safari on iOS). Trusting a fingerprint is also the equivalent of trusting the client, which is pretty well known as a bad idea. If an attacker has a financial incentive to target your site, they can easily write custom code to spoof the fingerprint itself, thus circumventing the check.
@@gringo5282 yeah, its a game of cat and mouse, availability and confidentiality are opposites in terms of cybersecurity and my solution was mostly done to dether "casual" hackers trying to get a simple oauth spoofing technique paired with a token expiration timer, i guess the real answer depends on the sensibility of your data and how secure you want it to be without affecting the user too much, 2fa auth on every single transaction like some crypto exchanges do it comes to mind, lol.
A long time a go for a very long time this was a good practice. You could also bind the token on a specific IP, so you will be still fine. You could also bind the token with a hardware ID and have another validation mechanism on top of the token match. Everything has it's good and bad side. Even considering the Cookie / Session Auth isn't really sure as you can still steal them with proper tools.
@@unfilledflagthats a good point but still a small edge case. However there are also short lived tokens that could be used with refreshing tokens but this is another hussle and story. So maybe just take the hardware UID to compare if the token is coming from the same device if you really want to make your token a bit more secure.
In a production app yeah sure I agree, but for your average hobby developer not necessarily. Its very easy to mess up or just don't know about CSRF and exploiting a site without a CSRF token is a lot easier than finding an XSS vulnerability in a web app built with any modern frontend framework. Supply chain attacks (or just not being careful and installing random packages) is a real threat and if your app is dealing with actually sensitive data you should invest the time to use a cookie based approach 100% of the time
Sure, I will use session storage (just kidding). On a serious note, it's worth noting that if you opt for local storage, all tabs end up sharing the same storage space. This means that if you're logged in as a regular user in one tab, and someone else logs in as an admin in another tab, the first tab unexpectedly assumes admin privileges as well. Just something to keep in mind!
It's important to remember the fulcrum of security. What are you protecting, and who are you protecting it from? Sometimes it's okay to do "bad practices" because the application doesn't require much security. As long as you are consciously making that choice. You need to know the rules before you break the rules.
I prefer cookies but you cannot make random requests with javascript to other domains. This is especially easy to lock down with CSP. Even though those libraries read local storage, it is difficult to get that out. Maybe with a browser extension but not likely to happen.
neither cookies nor localstorage tokens will be safe from malicious code in one of your npm packages the actual solution is to limit the amount of npm packages you install, and not load your app with a bunch of random packages with 10 downloads
Server should support storing token in secure, readonly cookie. Client should not have access to them. Server sets token in secure httponly cookie in login endpoint and server reads token from that cookie.
Cookies are more secure but not 100% safe. if you have an XSS, cookies will not help you much if it's a targeted attack, it won't be as easy as blindly stealing auth tokens from the localStorage but an XSS can still do A LOT.
@@codokit If you have an XSS, CSRF protection won't matter anymore because attacks will happen from the same site and not "cross-site", the attacker can make the user visit, fill & submit forms that already have CSRF tokens with them, he might even be able to silently craft & send XHR requests with CSRF tokens on behalf of the user, any cookies will also be sent automatically with the requests. in other words, if you have an XSS, you are screwed.
Doing it on the server's side can be dangerous too, cause if the server automatically reads the cookie to auth you, then what if someone wants to do csrf on you? And also what if your backend is php and you installed a sketchy library, wouldn't it work cause it's server side or what? Solution: Just install trusted libraries
I like your sarcasm, but you should devently make some warnings for new devs. Like you said, cookies are automaticly within every request. So also in the once made by mallicus code or xss So cookies are even more of a issue than localstorage. The "HTTP Only" Cookie was once an attempt to make it secure, but you just call fetch in js and the browser puts in the cookie anyways... Leaking it. However, you should always use the "Content-Security-Policy" Headers, this makes sure nothing can make requests to any webserver you did not manualy allow first. Then one of the only issues i can think of would be a XSS making a public post on your webpage (in case this is possible) with your secrets, but you can simply check if a post contains a currently valid secret and in case it does just don´t generate the post or invalidate the token like many apps already do it.
Good Topic. But where to store auth Tokens instead of localstorage? Or do you want to say don't use auth tokens? I don't understand your opinion clearly?
If you're using Laravel as a JSON API for a web application, do not use tokens - use the regular cookie based authentication as described here: laravel.com/docs/10.x/sanctum#how-it-works-spa-authentication If you're using Laravel as a JSON api for a mobile app, use token based authentication as described here: laravel.com/docs/10.x/sanctum#mobile-application-authentication If you have both a web app AND a mobile app use.... both. Use the cookie based auth routes for the web app login, and use the token based login routes for your mobile app. ---- I'm not familiar with quasar, but from the looks of it, I think you can just use cookies
Just don't manage your tokens with javascript at all. Set it on the server side in a cookie with httpOnly flag. It will be added to every request automatically, it will be handled by your backend, but won't be accessible via javascript.
there are third party extensions to show network packages. the one i found was ugly and cumbersome but it worked, and it wasn't detected by the website
Hi! right now I have to authenticate my app, and I was thinking about saving the jwt token in the localstorage, but seeing this video I know it's not the right thing to do. The only way I have to use my backend (symfony) is with fetch, I use Axios. How should I do the authentication?
I want to use session cookies, but my frontend and backend are in different docker containers, different ports, and possibly different domains, do you recommend any guide that i can follow to use session cookies?
@@bautistaigarzabal I had the exact same situation and I just went with localstorage. It's fine as long as you know the risks. Remember, even discord uses this method
@@bautistaigarzabal thats fine, you still can exchange cookies between each service, even though they are in different container, port, domain, zone, just set the cors on the server to allow subdomain/domain where you serve your frontend app to allow credentials, then each of requests fired via axios should have withCredentials set to true and you are good to go
I've often wondered about the security of local storage when using frames as well because the top page and page can use local storage for communication. Will be encrypting data here going forward.
@@aGj2fiebP3ekso7wQpnd1Lhd if you intent to keep an access token there it won't prevent an attacker from using it. they just won't know what is inside which is pointless since those tokens are usually random/encoded json of non critical info
oh so it's the self xss argument, as always. If you are already compromised, it doesn't matter where you store your tokens. And the cookies can be stolen using JS too
Very good explaination. But what if a npm package tracks all the requests that I am sending with body header and everything including response. How to be safe from that ?
Man, unfortunatley i dont handle auth for my app, my DB does and it stores them in localStorage, and, in some parts of my code extracting tokens from it is actually used. For example to auth the same user across different domains. Thanks for the video btw.
I mean, everything's on a spectrum. Local storage is more vulnerable to compromised JavaScript dependencies then a http only cookie. Of course, depending on how sophisticated the malicious dependency, it could make api calls to your backend while you're visiting the page in your browser, but that would have to be more targeted.
Sounds like snake oil, if you have XSS then you have already a compromised system. I find local storage more straight forward and I only need to care about XSS not some crazy CSRF or extra security headers.
@@qbasic16 The encrypted token will be in localStorage (aka Clinet) but the key to decrypt will be in server. Decrypt it then check if the encrypted token does match.
nice, but how the fuck are you store secure tokens in a server-less application. I mean literally, you only get an SPA without any code running on the server other than sending a static file. every single solution will expose the token in an easily accessible way.
Meaningless in what sense? As long as the token is valid proof of authentication, the attacker will be able to use it to impersonate the user they stole it from.
@@EpKjelltzer But adding the user ip, and browser or extra info should help mitigate those things, plus a refresh token stored on the server. I mean that's the usual JWT pattern. An attacker could decode it but the token should not be valid from anywhere