I went on a search of Node.js/Express.js authentication tutorials. All of them were incomplete or made a security mistake in some way that can potentially hurt new users. This post explores some common authentication pitfalls, how to avoid them, and what to do to help yourself when your tutorials don’t help you anymore. I am still searching for a robust, all-in-one solution for authentication in Node/Express that rivals Rails’s . tl;dr: Devise : RisingStack has reached out and in their tutorial, opting to move to bcrypt in their example codes and tutorials. Editing title to , as this post has improved some of these tutorials. Update (Aug 7) no longer stores passwords in plaintext Update (Aug 8): Your Node.js authentication tutorial is (probably) wrong Update (Aug 10): Dan McGhan found that one of the tutorials has addressed an issue that I had somehow missed in this documentation. I’ve omitted the graf for now, as Medium doesn’t allow for strikethrough. After all, I make mistakes, too. 😊 An addendum is placed at the end of this article. Update (May 27 2018): This post still gets a lot of views, but this post is now months old and may contain obsolete information. I have since resigned myself from trying to harden the Node ecosystem; it is throwing cups of water on a wildfire. Ecosystem growth is prioritized over security, and you have to decide whether or not those risks are acceptable to your organization. In my spare time, I’ve been digging through various Node.js tutorials, as it seems that every Node.js developer with a blog has released their own tutorial on how to do things , or, more accurately, . Thousands of front-end developers being thrown into the server-side JS maelstrom are trying to piece together actionable knowledge from these tutorials, either by cargo-cult-copypasta or gratuitous use of as they scramble frantically to meet the deadlines set for them by outsourcing managers or ad agency creative directors. the right way the way they do them npm install One of the more questionable things in Node.js development is that authentication is largely left as an exercise to the individual developer_._ The authentication solution in the Express.js world is , which offers a host of for authentication. If you want a robust solution similar to for Ruby on Rails, you’ll likely be pointed to , a startup who has made authentication as a service. de facto Passport strategies Plataformatec’s Devise Auth0 Compared to Devise, Passport is simply authentication middleware, and does not handle any of the other parts of authentication for you: that means the Node.js developer is likely to roll their own API token mechanisms, password reset token mechanisms, user authentication routes and endpoints, and views in whatever templating language is the rage today. Because of this, there are a lot of tutorials that specialize in setting up Passport for your Express.js application, and nearly all of them are wrong in some way or another, and none properly implement the full stack necessary for a working web application. : I’m not attempting to harass the developers of these tutorials specifically, but rather I am using their authentication mistakes to show security issues inherent in rolling your own authentication systems. If you are a tutorial writer, feel free to reach out to me once you’ve updated your tutorial. Let’s make Node/Express a safer ecosystem for new developers. Note Mistake one: credential storage Let’s start with credential storage. Storing and recalling credentials is pretty standard fare for identity management, and the traditional way to do this is in your own database or application. Passport, being middleware that simply says “this user is cool” or “this user is not cool”, requires the module for handling password storage in your own database, written by the same developer as Passport.js itself. passport-local Before we go down this tutorial rabbit hole, let’s remind ourselves of a by OWASP, which boils down to “store high-entropy passwords with unique salts and one-way adaptive cost functions”. Or, really, Coda Hale’s , even though . great cheat sheet for password storage bcrypt meme there’s some contention If you want to work with state of the art credential storage, is the winner of the Password Hashing Competition and now has . Unfortunately, documentation for the implementation of Argon2 for novice users in the Node.js ecosystem is still lacking, and given that this is targeted toward people reading tutorials, sticking with bcrypt for the next few months seemed an OK tradeoff. Note for experienced developers: really Argon2 some easy support in Node.js As a new Express.js and Passport user, my first place to look will be the example code for itself, which I can clone and extend. However, if I just copypasta this, I’m not left with too much, as there’s no database support in the example and it assumes I’m just using some set accounts. passport-local thankfully gives me a sample Express.js 4.0 application That’s OK, though, right? , the dev says, . Of course, the passwords for the example aren’t hashed in any way, . Credential storage isn’t even considered in this one. It’s just an Intranet application and I have four other projects assigned to me due next week and stored in plaintext right alongside the validation logic in this example Let’s google for another tutorial using . I find called “Node Hero”, but that doesn’t help me, either. They, too, , but it had . passport-local this quick tutorial from RisingStack in a series gave me a sample application on GitHub the same problems as the official one ( RisingStack is Ed. 8/7/17: now using bcrypt in their tutorial application.) You could accuse me of cherry-picking tutorials, and you’d be right, if cherry picking means selecting from the first page of Google results. Let’s choose the . This one is better, in that . The top result on Google, , also uses . Both of these are small, but 8 is really small. Most libraries these days use 12. when the original bcrypt paper was released. higher-ranked-in-results tutorial from TutsPlus passport-local it uses brypt with a cost factor of 10 for password hashing the tutorial from scotch.io bcrypt with a lesser cost factor of 8 bcrypt The cost factor of 8 was for administrator accounts eighteen years ago Password storage aside, neither of these tutorials implement password reset functionality, which is left as an exercise to the developer and comes with its own pitfalls. Mistake two: password reset A sister security issue to password storage is that of password reset, and none of the top basic tutorials explain how to do this at all with Passport. You’ll have to follow another. There are a thousand ways to fuck this up. The most common ways I have witnessed that people get password reset wrong are: Tokens that are based upon the current time are a good example. Tokens made by bad pseudorandom number generators are less obvious. Predictable tokens. Storing unencrypted password reset tokens in your DB means that if the DB is compromised, those tokens are effectively plaintext passwords. Generating a long token with a cryptographically secure random number generator stops remote brute force attacks on reset tokens, but it doesn’t stop local attacks. Reset tokens are credentials and should be treated as such. Bad storage. Not expiring your tokens gives attackers more time to exploit the reset window. No token expiry. Security questions are the data verification for a reset. Of course, then the developer has to choose . . While this may seem like security overkill, the email address is something you have, not something you know, and conflates the authentication factors. Your email address becomes the key to every account that just sends a reset token to email. No secondary data verification. de facto good security questions Security questions have their own problems If you’re new all of this, try OWASP’s . Let’s get back to what the Node world has to offer for us on this. Password Reset Cheat Sheet We’ll divert to for a second and , to see if anyone’s made this. There’s a five-year-old package from the (generally awesome) substack. On the Node.js timeline this module is jurassic, and if I wanted to nitpick, , so . Also, it doesn’t use Passport, so we’ll move on. npm look for password reset Math.random() is predictable in V8 it shouldn’t be used for token generation Stack Overflow isn’t of too much help, as developer relations from a company called Stormpath loved plugging their IaaS startup on every imaginable post regarding this. and they have . However, all of this is for naught as Stormpath is defunct, August 17, 2017. Their documentation also popped up everywhere a blogvertisement on password reset, as well and it shuts down entirely Alright, back to Google, for the only tutorial that seems to exist out there. We’ll take for the Google search Here is our old friend again, with an even smaller cost factor of 5 used in the text, which is too small of a cost factor for modern use. the first result express passport password reset. bcrypt far However, this tutorial is pretty solid compared to others in that it uses to generate truly random tokens, and expires them if they aren’t used. However #2 and #4 of the practices above aren’t honored by this comprehensive tutorial, and thus the password tokens themselves are vulnerable to authentication mistake number one, credential storage. crypto.randomBytes Thankfully, this is of limited use thanks to the reset expiry. However, these tokens are especially fun if an attacker has read access to the user objects in the DB via BSON injection or can access Mongo freely due to misconfiguration. The attacker can just issue password resets for every user, read the unencrypted tokens from the DB, and set their own passwords for user accounts instead of having to go through the costly process of dictionary attacks on bcrypt hashes with a GPU rig. Mistake three: API tokens API tokens are credentials. They are just as sensitive as passwords or reset tokens. Most every developer knows this and tries to hold their AWS keys, Twitter secrets, etc. close to their chest, however this doesn’t seem to transfer into the code being authored. Let’s use for API credentials. Having a stateless, blacklistable, claimable token is better than the old API key/secret pattern that has been used for the better part of a decade. Perhaps our junior Node.js dev has heard of JWT somewhere before, or saw and decided to implement the JWT strategy. In any case, JWT is where everyone seems to be moving in the Node.js sphere of influence. (The venerable but I’m afraid that ship has sailed here.) JSON Web Tokens passport-jwt Thomas Ptacek will argue that JWT is bad We’ll search for on Google, and then find ’s tutorial which is the first tutorial result. Unfortunately, this doesn’t actually help us at all, since it doesn’t use Passport, but while we’re here we’ll quickly note the mistakes in credential storage: express js jwt Soni Pandey User Authentication using JWT (JSON Web Token) in Node.js We’ll store the . JWT key in plaintext in the repository We’ll . This means that I can get the encryption key and decrypt all of the passwords in event of a breach. The encryption key is shared with the JWT secret. use a symmetric cipher to store passwords We’ll use AES-256-CTR for password storage. We shouldn’t be using AES to start, and this mode of operation doesn’t help. I am not sure why this mode specifically was chosen, but . the choice alone leaves the ciphertext malleable Welp. Let’s back out to Google and we find the next tutorial. Scotch, which did an OK job with password storage in their passport-local tutorial, for this example. just ignores what they told you before and stores the passwords in plaintext Uh, we’ll give that a pass for brevity, but it doesn’t help the copypasta crew. That’s because more interestingly, this tutorial . also serializes the mongoose User object into the JWT Let’s clone the Scotch tutorial repository, follow the instructions, and run it. After a or three from Mongoose, we can hit to create the user, then get a token by posting to /api/authenticate with the default credentials of “Nick Cerminara” and “password”. A token is returned, as displayed from Postman. DeprecationWarning http://localhost:8080/setup Note that JSON Web Tokens are signed, but not encrypted. That means that big blob between the two periods is a Base64-encoded object. Quickly decoding it, we get something interesting. I love my passwords in plaintext in tokens. Now, anyone that has has your password, as well as whatever else is stored in the Mongoose model. Given that this one came over HTTP, I could have sniffed it off of the wire. even an expired token What about the next tutorial? The next tutorial, contains the same information disclosure vulnerability_._ The next tutorial from a startup called . At this point, I gave up looking. Express, Passport and JSON Web Token (jwt) Authentication for Beginners , SlatePeak does the same serialization Mistake four: rate limiting As I alluded to above, I did not find a mention of rate limiting or account locking in any of these authentication tutorials. Without rate limiting, an adversary can perform online dictionary attacks in which a tool like is run in hopes of gaining access to an account with a weak password. Account lockout also helps with this problem by requiring extended login information from a user the next time they log in. Burp Intruder Remember, rate limiting also helps availability. is a CPU-intensive function, and without rate limiting functions using bcrypt become an application-level denial of service vector, especially at high work factors. Multiple requests for user registration or login password checking are an easy way to turn a lightweight HTTP request into costly time for your server. bcrypt While I do not have a tutorial I can point to for these, there are tons of rate limiting middlewares for Express, such as , , and . I cannot speak to the security of these modules and have not even looked at them; generally I and allowing or whatever your load balancer is. express-rate-limit express-limiter express-brute recommend running a reverse proxy in production rate limiting to requests to be handled by nginx Authentication is hard. I’m sure the tutorial developers will defend themselves with “This is just meant to explain the basics! Surely nobody will do this in production!” However, I cannot emphasize enough . This is true when code is put out there in your tutorials. People will take your word for it — after all, you have more expertise than they do. just how false this is especially do Copypasta from tutorials likely get you, your company, and your clients in authentication trouble in the Node.js world. If you really need strong, production-ready, all-in-one authentication libraries, go back to something that holds your hand better, has better stability, and is more proven, like Rails/Devise. If you’re a beginner, don’t trust your tutorials. will The Node.js ecosystem, while accessible, still has a lot of sharp edges for JavaScript-based developers needing to write production web applications in a hurry. If you have a front-end background and don’t know other programming languages, I personally believe it is easier to pick up Ruby and stand on the shoulders of giants than it is to quickly learn how not to shoot yourself in the foot when writing these types of things from scratch. If you’re a tutorial writer, update your tutorials, the boilerplate code. This code will become copypasta into others’ production web applications. please especially If you are a die-hard Node.js developer, hopefully you’ve learned a few things not to do in your authentication system you’re rolling with Passport. You will likely get something wrong. I haven’t gotten close to covering all of the ways to get it wrong in this one post. It shouldn’t be your job to roll your own auth for your Express application. There should be something better. If you’re interested in better securing the Node ecosystem, please DM me on Twitter. @_micaksica This post was brought to you by espresso because I’m out of sake. Addendum (last updated August 10, 2017) This post started out of frustration from the problems I had been seeing, and has somehow taken on a life of its own. It has received more responses, more feedback, and caused more controversy than I had expected it to. Clearly this has been a point of frustration for other developers. If you find errors in this post, please message me and we’ll work to fix them. What we don’t need is another misguided post. That’s the opposite of what we need. However, I implore you: let’s not just work on this post. Let’s make the ecosystem better in the future. If you find errors or have other practices, things you’ve learned in production, or simply improvements, feel free to DM me. containing “cheat sheet” style best practices around authentication with Node/Express as a patch until there are proven, robust solutions. It seems that the next best step for us is to write a living document To that end, I’ve started . Feel free to file issues, your recommendations, or make some pull requests to get us started. I’ll work on it as soon as I get a chance this upcoming weekend. a repository on GitLab