1. 1

    Cookies are insecure because they are sent with every request automatically making them susceptible to CSRF attacks.

    1. 2

      CSRF prevention is well-known and widely implemented in pretty much every web framework/language. Submitting a random string in forms is pretty standard now. CSRF used to be a big problem in the late 90s but hasn’t been an issue since then.

      1. 1
        1. 1

          Thats quite interesting. Thanks for the link.

      1. 18

        The article seems to put forward two seemingly separate arguments, that localStorage is not a good API and that localStorage is insecure. I see these as separate issues. I don’t have a particularly strong opinion about whether or not the API is good or bad, it is what it is. However I do think that calling localStorage insecure is unfair. It’s no more secure or insecure than anything else in the browser: if you’re giving 3rd parties access to your website you’re inherently trusting them.

        Things are getting completely out of hand.

        Almost every day I stumble across a new website storing sensitive user information in local storage and it bothers me to know that so many developers are opening themselves up to catastrophic security issues by doing so.

        […]

        And the thing about local storage is that it is not secure! Not at all! Everyone who uses local storage to store sensitive information such as session data, user details, credit card info (even temporarily!) and anything else you wouldn’t want publicly posted to Facebook is doing it wrong.

        [yadda yadda yadda XSS]

        So to err on the side of caution and dramatically reduce your risk for a security incident: don’t store anything sensitive in local storage.

        The argument boils down to “don’t use localStorage because it’s susceptible to XSS attacks” and I disagree wholeheartedly. XSS is basically game-over in the context of a website, so building your application under the assumption you’re vulnerable to XSS is, I think, misguided. I think it’s better to work your darnedest to prevent XSS attacks, and between Content Security Policy (CSP) and Subresource Integrity (SRI) I think developers have the tools they need (in modern browsers, granted*) to do so.

        * That said, the author talks about using SameSite=strict, which has poorer support than CSP 2, let alone CSP 1.

        Don’t tell me that JWTs are “stateless” and “fast” and you have to use local storage to store them: you’re wrong!

        JWTs are stateless and they are “fast”. Neither of those things are negated by the fact that you can use localStorage to store them.

        NOTE: For those of you who made it this far who are wondering if I didn’t read the article in its entirety because the author has notes at the bottom about CSP and SRI, I think CSP and SRI are solutions to the problems they’re describing and they seem to have tacked on hand-wavy dismissals at the end. The combination of CSP and SRI prevent XSS. The people who can’t use SRI because of 3rd-party ad networks or whatnot are inherently trusting those resources.

        The article would be better to say “if you’re blindly trusting 3rd-party code, know that they can read your localStorage values and think twice” instead of trying to make sweeping the claim that localStorage is insecure.

        1. 4

          The article seems to put forward two seemingly separate arguments, that localStorage is not a good API and that localStorage is insecure. I see these as separate issues. I don’t have a particularly strong opinion about whether or not the API is good or bad, it is what it is. However I do think that calling localStorage insecure is unfair. It’s no more secure or insecure than anything else in the browser: if you’re giving 3rd parties access to your website you’re inherently trusting them.

          You hit the nail on the head. Sorta.

          I don’t really think local storage is bad or anything, but it just seems like it has a really limited use case. What really annoys me and what prompted me to write this article is the common pattern now-a-days (adopted by 99% of web developers) where you authenticate a user, store their usre info in a JWT in local storage, and somehow believe that makes the site more secure than a cookie.

          I just wanted to emphasize how widespread XSS is, how easy it can be for your most sensitive data to be stolen (your session token), and why it’s not a good idea to trust local storage for sensitive information.

          Finally – towards the bottom of your response you mention the thing about JWTs being stateless and fast as incorrect. JWTs are stateless (and fast, sure, once they’ve been sent to the server), but:

          • Using a stateless JWT makes you store a lot of user info in a token which ends up making it larger (>4mb for almost all my sample apps). This means I’m now required to store it in local storage as cookies cap out at 4k.
          • Using JWTs requires you to build centralized revocation list patterns into your server side app (unless you don’t really care), and that requires centralization again – so you lose the stateless benefits of the JWT.
          • Using JWTs in local storage means you’ve now got to send a much larger amount of data to a web server on every request. This feels like a bad tradeoff to me. Datacenter networks will always be faster than consumer networks, and it doesn’t make sense to me to push the burden of extra data transmission to the client.
          • Using JWTs for auth means you’re directly reducing security (speed/security tradeoff). The best practice in the security world is to always perform authn/authz in realtime to get guarantees. This is impossible by design in JWTs.

          I’m just a fan of using normal sessions and keeping things simple. I wanted to present an argument for why I think it’s a better strategy and give people something to think about before blindly choosing to store sensitive info in local storage =)

          1. 1

            You missed “localstorage auth can only work with JavaScript, so your site probably sucks to use”.

            Having to load the page, parse the script, then send another request and put the results into the DOM is much slower than just serving the content in the first place.

            1. 1

              What really annoys me and what prompted me to write this article is the common pattern now-a-days (adopted by 99% of web developers) where you authenticate a user, store their usre info in a JWT in local storage, and somehow believe that makes the site more secure than a cookie.

              Maybe my point is this: the “security” of cookies and the “security” of localStorage are incomparable. I don’t think it’s correct to say one is more secure than the other. Everything in software development is about tradeoffs, to your point. They have different attack vectors and require different approaches to securing their usage. Cookies are susceptible to CSRF and localStorage is susceptible to XSS (both probably amongst other things).

              How about I propose this hypothetical: if XSS wasn’t a thing, would it then be fine to store “sensitive” information in localStorage? I think yes. If so, I think it’s more useful to work towards preventing XSS than assuming game over.

              I just wanted to emphasize how widespread XSS is

              That’s a good thing to do, I agree that web developers sometimes don’t realize how susceptible their applications are. That said, I again think the article would be better to say “if you’re blindly trusting 3rd-party code, you’re susceptible to XSS, they can read your localStorage values, and think twice about using localStorage” instead of trying to make sweeping the claim that localStorage is insecure.

          1. 3

            Do you have any examples of sensitive data being leaked because of local storage?

            I also have some bad news for you and its name is PWA.

            1. 2

              PWA’s don’t prevent you from using cookies for sessions.

              1. 1

                There are a lot of examples: I work in security for a living. I recently discovered a pretty well-known vendor of analytics services who had their CDN’d JS scripts (that tons of customers have embedded in their websites) sitting on a publicly accessible (with write access!) S3 bucket.

                For PWAs: I honestly have no idea how to secure those things. It’s hard =/

                1. 1

                  Are there examples I can look at? That I can use to make the case myself. Any documented instances of the threat vectors mentioned in the article.

              1. 3

                Why focus so much on local storage? You shouldn’t store sensitive information anywhere in your browser.

                1. 3

                  I solely focused on that because this is the trend I see: developers building web apps storing JWTs in localStorage. I could have focused on the larger picture but it was easier to pick on just this one pattern as it is so common.

                  1. 1

                    I’ve always stored them in cookies tbh, and from a security perspective - there is nothing wrong w/ storing JWTs in cookies or local storage. That’s literally what they’re for.

                  2. 3

                    It’s necessary to store auth tokens so you don’t need to type your password with every request.

                  1. 7

                    I‘m not convinced that the current trend to put authentication info in local storage is entirely driven by the thought of being able to bypass the EU cookie banner thing. I think it‘s more related to the fact that a lot of people are jumping on the JWT bandwagon and that you need to send that JWT over an Authorization header rather than the cookie header.

                    Also, often, the domain serving the API isn‘t the domain the user connects to (nor even a single service in many cases), so you might not even have access to a cookie to send to the API.

                    However, I totally agree with the article that storing security sensitive things in local storage is a very bad idea and that httponly cookies would be a better idea. But current architecture best-practice (stateless JWT tokens, microservices across domains) make them impractical.

                    1. 4

                      Hey! You are correct in that this isn’t the main reason people are doing this – but I’ve spoken to numerous people who are doing this as a workaround because of the legislation which is why I wrote the article =/

                      I think one way of solving the issue you mention (cross-domain style stuff) is to use redirect based cookie auth. I’ve recently put together a talk which covers this in more details, but have yet to write up a proper article about it. It’s on my todo list: https://speakerdeck.com/rdegges/jwts-suck-and-are-stupid

                      1. 2

                        Ha! I absolutely agree with that slide deck of yours. It’s very hard to convince people though.

                        One more for your list: having JWTs valid for a relatively short amount of time but also provide a way to refresh them (like what you’d do with an oauth refresh token) is tricky to do and practically requires a blacklist on the server, reintroducing state and defeating the one single advantage of JWTs (their statelessnes, though of course you can have that with cookies too)

                        JWTs to me feel like an overarchitectured solution to an already solved problem.

                        1. 1

                          There’s a third use case: services that are behind an authentication gateway (like Kong) and whenever a user is doing an authenticated request then the JWT is injected by the gateway into the request headers and passed forward to the corresponding service.

                          But yes, a lot of people are using $TECHNOLOGY just because it’s the latest trend and discard “older” approaches just because they are no longer new which is quite interesting because we today see a resurgence of functional languages which are quite old, but I digress.

                        2. 2

                          you need to send that JWT over an Authorization header rather than the cookie header.

                          Well, you don’t need to, but many systems require you to. It’s completely possible — although it breaks certain HTTP expectations — to use cookies for auth² is after all quite an old technique.

                          1. 1

                            This is true – you could definitely store it in a cookie – but there’s basically no incentive to do so. EG: Instead just use a cryptographically signed session ID and get the same benefits with less overhead.

                            The other issue w/ storing JWTs in cookies is that cookies are limited to 4kb of data, and JWTs often exceed that by their stateless nature (trying to shove as much data into the token as possible to remove state).

                          2. 1

                            Could you point me to some sort of explanation of why using localStorage is bad for security? Last time I looked at it, it seemed that there was no clear advantage to cookie based storage: http://blog.portswigger.net/2016/05/web-storage-lesser-evil-for-session.html

                            1. 2

                              Just as the article says: if you mark the session cookie as http only, then an XSS vulnerability will not allow the token to be exfiltrated by injected script code.

                              1. 1

                                Are we reading the same article? What I see is:

                                • “The HttpOnly flag is an almost useless XSS mitigation.”
                                • “[Web storage] conveys a huge security benefit, because it means the session tokens don’t act as an ambient authority”
                                • “This post is intended to argue that Web Storage is often a viable and secure alternative to cookies”

                                Anyway, I was just wondering if you have another source with a different conclusion, but if not, it’s OK.

                                1. 3

                                  I disagree with the author of that article linked above. I’m currently typing out a full article to explain in more depth – far too long for comments.

                                  The gist of it is: HttpOnly works fine at preventing XSS. The risk of storing session data in a cookie is far less than storing it in local storage. The attack surface is greater there. There are a number of smaller reasons as well.

                                  1. 1

                                    Great, I would appreciate a link (or a Lobsters submission) when you’ve written it.

                          1. 5

                            Great guide! One small note; you should use AWS credentials associated with an IAM role that has the least privileges required to get the job done. In this case, using creds from an IAM role that could only write to the particular S3 bucket and invalidate the specific CloudFront domain would be most secure.

                            1. 4

                              This is correct – although right now there is a bug with IAM cloudfront rules, which means you must have full cloudfront access to do even a simple invalidation :(

                            1. 9

                              I’m Randall Degges. I’ve been programming since I was ~12 years old, and I just turned 28. I suppose if I add that up, it would mean I’ve been coding (poorly, heh) for ~16 years now.

                              I’m the lead Developer Evangelist now over at Stormpath (https://stormpath.com). At Stormpath, I spend most of my time hacking on open source authentication and authorization libraries in Python, Node.js, and Go. I also give tech talks, go to meetups, and do the social outreach stuff you’re all familiar with: blogs, screencasts, etc.

                              It’s a fun position!

                              Over the last 3 years or so, my hobby has been bodybuilding. I spent all my life being incredibly overweight, and decided that if I could write code and do complex software stuff, then I should at the very least be able to figure out how to diet and look good. After doing a lot of research, slowly learning the ins-n-outs, etc., I think I’ve finally made it =)

                              Here’s me when I got married, vs me now:

                              Overall, it’s been insanely great. Feels so good being in shape now vs before. It’s been really fun to push myself hard to reach my goals ^^

                              1. 2

                                Wow, congratulations on all the weight loss and muscle gain! You look great!

                                1. 2

                                  Thanks! <333

                              1. 5

                                I started lifting weights a few years ago because I was pretty fat from sitting at my desk all day, and decided I wanted to get into shape.

                                It’s been a few years now, and I’m sort of addicted to the feeling of becoming bigger and stronger. I started learning about bodybuilding, nutrition, etc., and now do it as a part-time hobby!

                                1. 2

                                  I love this story. I’ve read it many times over the years, but it always continues to inspire me.

                                  1. 8

                                    Hey Carlos!

                                    I’m also quite involved in the telephony world, I’m the creator of OpenCNAM: http://www.opencnam.com/

                                    I’m quite familiar with playing data before the call is Answered using Asterisk, but my understanding of the US law is that doing that is technically considered ‘toll fraud’ by the big phone companies. You might want to make sure you’re safe from prosecution by the big US telcos (wouldn’t want ya to get into trouble).

                                    Looks like a very cool service otherwise though! All the best

                                    1. 5

                                      Thanks for the advice. This is definitely a gray area; some providers have specific ToS against missed calls, others are happy with them but charge for missed calls, and we’re in a partnership with a local SIP provider who knows about the project and has reached an agreement with us.

                                      Currently our major problem is the lack of interoperability between networks – especially caller ID, of which I’m sure you know the pain. I’m astonished that calling from one network in one country to another abroad even works. Many of them ignore basic SIP commands or just do the opposite (603 vs 486, you know)

                                      Anyway, I could ramble about this all day. Telco trouble is one of our major worries, for sure.

                                      1. 1

                                        Welp, best of luck to ya! Tis a very cool project, I hope things work out well.

                                    1. 2

                                      I loveeee this site – IMO, it’s one of the best python resources for new python developers. Matt is also a personal friend, and really awesome guy. +1

                                      1. 8

                                        I actually run this service =) Surprised to see it on here! Am happy to answer any questions (if there are any) =)

                                        1. 2

                                          Oh, that is neat! :) I hadn’t heard of it, but yes, it looks as though it fills a real need. I like that the examples are for a broad range of languages.

                                          1. 3

                                            Ya! I started it a while back as a small need – it’s useful for dynamic DNS type stuff, and auto-provisioning tools. I tried to make the libraries really robust and fool-proof. They all use exponential backoff and retry logic, and try to smartly handle all network errors / etc =)

                                            1. 2

                                              Nice :)

                                          2. 2

                                            This looks neat, I usually hit http://whatismyip.akamai.com when I need it programatically as they just return the plaintext, but I never remember the URL and have to go search my browser history for it every damn time!

                                            I do have a small suggestion for the ruby example however, open-uri is easy to use, but devs who use it should be aware what they’re doing with it - if you’re using it to load in external resources you could be opening yourself up to a remote code exploit. http://sakurity.com/blog/2015/02/28/openuri.html has more information.

                                            Might I suggest you just drop back to net/http (which is all open-uri is wrapping) instead for the example? It’s still pretty small:

                                            require "net/http"
                                            
                                            ip = Net::HTTP.get(URI("https://api.ipify.org"))
                                            puts "My public IP Address is: " + ip
                                            
                                            1. 2

                                              Thanks for the suggestion! I’m not a ruby guy, so the ruby example was cobbled together. I just updated the code snippets! <33

                                              1. 2

                                                Awesome, thanks! I should’ve noticed it was on github and opened an issue there heh. :-)

                                              2. 1

                                                I found http://icanhazip.com to be easy to remember. It also return the IP in text/plain.

                                            1. 2

                                              Very nice documentation site for this, that was a nice touch. Although if you must do content negotiation via query string, I wish it at least supported Accept headers as a fallback. Now I’m tempted to learn enough Go to open a PR.

                                              1. 2

                                                I’ll add this in =) I’ve been meaning to do it. Initially I wrote this to solve a small need, and got a lot of requests for the whole querystring approach, but I’d like to make it support the pure REST stuff too ^^