Once I went to search for authentication tutorials in Node.js / Express.js, but unfortunately I couldn’t find one that would suit me completely. Some were incomplete, some contained security flaws that could well harm inexperienced developers.
I’ll say right away that I’m still looking for a reliable, comprehensive authentication solution in Node / Express that can compete with
Devise for Rails. However, the depressing situation in the field of manuals prompted me to prepare this material. Here I will examine some of the most common errors in the field of authentication and tell you how to avoid them.

Above, I talked about "inexperienced developers." Who are they? For example, there are thousands of front-end programmers abandoned in the whirlpool of server-side JS, who are trying to gain practical experience from the manuals, or simply copy-paste everything that comes to hand and install everything using npm install. Why don't they invest time in a serious study of the question? The fact is that they are not copying from a good life, they have to go out of their way to meet the deadlines set by outsourcing managers, or someone like creative directors of advertising agencies.
')
In fact, while I was looking for a suitable authentication guide, I was filled with the feeling that every Node programmer who has a blog published his own tutorial on how to “do it right”, or, more precisely, about “how it's done. "
Another controversial thing about developing for Node.js is the absence of some kind of comprehensive, reliable authentication solution. This question is generally regarded as something like an exercise for a programmer. The de facto standard for Express.js is
Passport , however, this solution offers only a set of authentication strategies. If you need a reliable solution for Node, like
Platformatec Devise for Ruby on Rails, you will probably have to turn to
Auth0 - a startup that offers authentication as a service.
Passport, unlike full-blown Devise, is an intermediate software layer that, by itself, does not cover all parts of the authentication process. Using Passport, the Node developer will have to create its own API for the token mechanism and to reset the password. It will have to prepare routes and user authentication endpoints. It also contains the creation of interfaces using, for example, a certain popular template language. That is why there are many tutorials that are aimed at helping to install Passport for Express.js applications. Almost all of them contain certain errors. None of them allows you to create a full-scale solution required for a running web application.
I would like to point out that I am not going to attack the specific creators of these guides, rather, I use their mistakes in order to demonstrate the security problems associated with deploying your own authentication systems. If you are the author of such a guide,
let me know if you make changes to your guide after reading this material. Let's make the Node / Express ecosystem safer and more accessible for new developers.
Error one: credential store
Let's start with the credential storage. Writing and reading credentials are quite common tasks in the field of authentication management, and the traditional way to solve these tasks is to use your own database. Passport is middleware that simply tells our application: “this user passed the test,” or: “this user failed the test,” requiring the
passport-local module to work with the password store in the local database. This module is written by the same developer as Passport.js itself.
Before we go down into this rabbit hole of study guides, consider the excellent
password storage crib prepared by OWASP, which boils down to storing high-entropy passwords with unique salt and using one-way adaptive hashing functions. Here you can recall the
bcrypt-meme from codahale.com, even though there are some
disagreements on this issue.
I, in search of what I need, repeating the path of the new user Express.js and Passport, at first glanced at the examples to passport-local itself. There was an Express 4.0 application template, which I could copy and expand to fit my needs. However, after simply copying this code, I didn’t get many useful things. For example, there was no database support subsystem. The example implied the simple use of some set of accounts.
At first glance, everything seems to be normal. Before us is the usual intranet application. The developer who takes advantage of him is loaded to the last, he does not have time to seriously improve something. It doesn't matter that the passwords in the example are not hashed. They are stored as
plain text right next to the validation logic code. And the credential storage is not even considered here. Wonderful get authentication system.
Let's look for another passport-local tutorial. For example, I came across
this material from RisingStack, which is included in the Node Hero guide series. However, this publication did not help me at all. She also gave
an example of the application on GitHub, but had the same problems as the official manual. Here, however, it should be noted that on August 8, it became known that RisingStack
now uses bcrypt in its demo application.
Next, here is another result from Google, issued on request
express js passport-local tutorial
. The manual was written in 2015. It uses Mongoose ODM and reads the credentials from the database. It has everything, including integration tests, and, of course, another template that can be used. However, Mongoose ODM stores passwords using the
String data type , as in previous tutorials, as plain text, only this time in a MongoDB instance. And everyone knows that instances of MongoDB are usually
very well protected .
You can accuse me of a biased selection of tutorials, and you will be right if a biased selection means clicking a link from the first page of a Google issue.
Now let's take the material from the top of the search results page -
the passport-local manual from TutsPlus. This manual is better; bcrypt is
used here with a coefficient of 10 for hashing passwords and slows down synchronous hash checks using
process.nextTick
.
The top most result in Google, this
guide from scotch.io , which also uses bcrypt with a lower labor intensity factor of 8. Both 8 and 10 are few, but 8 are very few. Most modern bcrypt libraries use 12. The coefficient of complexity 8 was good for administrative accounts
eighteen years ago , immediately after the release of the first bcrypt specification.
If you don’t even talk about storing sensitive data, none of these manuals showed the implementation of a password reset mechanism. This most important part of the authentication system, in which a lot of pitfalls, was left as an exercise for the developer.
Error two: password reset system
A similar security issue is password reset. None of the guides at the top of the search results says absolutely nothing about how to do this with Passport. To find out, you have to look for something else.
There are thousands of ways to spoil everything in resetting a password. Here are the most common errors in the solution of this problem, which I happened to see:
- Predictable tokens . Current time based tokens are a good example. Tokens based on a bad pseudo-random number generator, although they look better, do not solve the problem.
- Failed data storage . Storing unencrypted password reset tokens in a database means that if it is cracked, these tokens are equivalent to passwords stored in plain text. Creating long tokens using a cryptographically secure pseudo-random number generator helps prevent remote attacks on password reset tokens using brute force, but does not protect against local attacks. Tokens to reset a password should be taken as credentials and treated with them accordingly.
- Tokens whose validity does not expire . If the tokens do not expire, the attacker has time to use the password reset time window.
- No additional checks . Additional questions when resetting a password is a de facto data verification standard. Of course, this works as it should only if developers choose good questions. Such questions have their own problems . It should be said about the use of e-mail for password recovery, although arguments about this may seem like an unnecessary reinsurance. Your email address is what you have, not what you know. It combines various authentication factors. As a result, the email address becomes the key to any account that simply sends a password reset token to it.
If you’ve never experienced it all, take a look at the OWASP
cheat sheet for resetting passwords. Now, having discussed general issues, let's get to the specifics, see what the Node ecosystem has to offer.
Let us briefly go to npm and
see if anyone has made a password resetting library. Here, for example, is
a five-year-old
package from a generally wonderful substack publisher. Given the speed of Node development, this package resembles a dinosaur, and if I wanted to play around with the little things, I could say that the
Math.random()
function is
predictable in V8, so it
should not be used to create tokens. In addition, this package does not use Passport, so we go further.
Stack Overflow here also did not help much. As it turned out, the developers at Stormpath love to write about their IaaS startup in any post that is somehow related to this topic. Their
documentation also pops up everywhere, they also promote their blog, where there are
materials on resetting passwords. However, reading all this is a waste of time. Stormpath is a non-working project, it
closes on August 17, 2017.
Okay, back to Google search. In fact, the feeling that we are interested in the topic disclosed in a single material. Take the
first result found on the request
express passport password reset
. Here again we meet our old friend bcrypt, with an even lower coefficient of labor intensity equal to 5, which is significantly less than is needed in modern conditions.
However, this tutorial looks rather holistic compared to others, as it uses
crypto.randomBytes
to create truly random tokens that will expire if they have not been used. However, points 2 and 4 of the above list of errors when resetting the password are not taken into account in this serious guide. Tokens are not stored securely - we recall the first mistake of the authentication guides related to the storage of credentials.
Well at least that the tokens stolen from such a system have a limited duration. However, working with these tokens is very fun if the attacker has access to user objects in the database through a BSON injection, or there is free access to Mongo due to incorrect DBMS settings. The attacker can simply start the process of resetting the password for each user, read the unencrypted tokens from the database and create their own passwords for user accounts, instead of engaging in a resource-intensive dictionary attack on bcrypt hashes using a powerful computer with several video cards.
Error Three: API Tokens
API tokens are also credentials. They are as important as passwords and password reset tokens. Practically all developers know this and try to keep their AWS keys very securely, access codes to Twitter and other similar things, however, this is often not the case with the programs they write.
We use the
JSON Web Tokens (JWT) system to create API access credentials. Using stateless tokens that can be added to blacklists and need to be requested is better than the old API key / secret template that has been used in recent years. Perhaps our beginner Node.js developer heard about JWT somewhere, or even saw the passport-jwt package and decided to implement the JWT strategy in his project. In any case, JWT is the place where it seems that everyone falls under the influence of Node.js. (The venerable Thomas Birdie
declares that JWT is bad, but I doubt anyone will hear it).
According to
express js jwt
for Google and open the first material in the search results,
Sony Pandy ’s
guide on user authentication using JWT. Unfortunately, this material will not help us, since Passport is not used in it, but while we are looking at it, we note some errors in the storage of accounting data:
- JWT keys are stored as plain text in the GitHub repository.
- For storing passwords a symmetric cipher is used . This means someone can take possession of the encryption key and decrypt all passwords. In addition, there is an incorrect relationship between the encryption key and the JWT secret key.
- Here, for encrypting data in the password store, the AES-256-CTR algorithm is used. AES should not be used at all, and this variant of it does not change anything. I don’t know why this particular algorithm was chosen, but this alone makes the encrypted data vulnerable .
Yeah ... Let's go back to Google and look for more manuals. The scotch.io resource, which, in the passport-local manual, did a wonderful job with the password store, simply
ignores its own ideas and stores the passwords in a new example as plain text.
However, I decided to give this guide a chance, although it cannot be recommended to copy-paste lovers. This is because of one interesting feature, which is that it
serializes the Mongoose
user object in JWT.
We clone the repository of this guide, following the instructions will deploy and run the application. After several Mongoose
DeprecationWarning
you can go to
http://localhost:8080/setup
and create a user. Then, by sending the credentials - “Nick Cerminara” and “password” to
/api/authenticate
, we will receive a token. We will look at it in Postman.
JWT token received from the program described in scotch.ioNotice that the JWT token is signed but not encrypted. This means that a large fragment of binary data between two points is an object in Base64 encoding. Quickly decode it and something interesting will open before us.
What could be better than a password in plain text?Now, anyone who has a token, even one that has expired, has the user's password, and at the same time everything else that is stored in the Mongoose model. Given that the token is transmitted via HTTP, you can take possession of it with the help of a regular sniffer.
How about another tutorial?
It is designed for beginners and is dedicated to authentication using Express, Passport and JWT. It has the same vulnerability associated with information disclosure.
The following tutorial , prepared by the SlatePeak startup, performs the same serialization. At this stage, I stopped searching.
Error four: limiting the number of authentication attempts
I did not find any mention of limiting the number of authentication attempts or of blocking an account in any of the reviewed guides.
Without limiting the number of authentication attempts, an attacker can perform an online dictionary-based attack, for example using
Burp Intruder , in the hope of gaining access to an account with a weak password. Locking your account also helps in solving this problem by requiring you to enter additional information on the next login attempt.
Remember that limiting the number of authentication attempts contributes to the availability of the service. So, using bcrypt creates a serious load on the processor. Without limitation, functions called bcrypt become the application-level denial of service vector, especially when using high values of labor intensity. As a result, the processing of multiple requests for user registration or login with a password check creates a high load on the server.
Although I don’t have a suitable tutorial on this topic, there are many supporting libraries to limit the number of requests, such as
express-rate-limit ,
express-limiter , and
express-brute . I can not talk about the security level of these modules, I did not even study them. In general, I would recommend to use a
reverse proxy on work systems and transfer processing to limit the number of requests to
nginx or any other load balancer.
Results: authentication is not an easy task
Most likely, the authors of the study guides will defend themselves with the words: “This is just an explanation of the basics! Sure, no one will use this in production! ” However, I can not point out that these words do not correspond to reality. This is especially true if a code is attached to the tutorials. People believe the words of the authors of the manuals, who have much more experience than those who read the manuals.
If you are a novice developer, do not trust the tutorials. Copy-and-paste code from such materials will surely lead you, your company, and your clients to problems in the field of Node.js authentication. If you really need robust, ready-to-use, production, comprehensive libraries for authentication, take a look at something that you are comfortable using, at something that is more stable and better time-tested. For example - on a bunch of Rails / Devise.
The Node.js ecosystem, despite its availability, still carries many dangers for JS developers who need to urgently write a web application to solve real-world problems. If your experience is limited to the front-end, and you don’t know anything except JavaScript, I’m personally convinced that it’s easier to take Ruby and stand on the shoulders of giants instead of quickly learning how not to shoot yourself off by programming these solutions from scratch for Node.
If you are the author of the tutorial, please update it, especially for the template code. This code will go into production.
If you are a committed Node.js developer, I hope you have found something useful in my story about what is best not to do in your Passport-based authentication system. Surely, if you already have such a system, something is done wrong in it. I'm not saying that my material covers all possible authentication errors. Creating an authentication system for an Express application is the task of a developer who understands all the details of a specific project. As a result, he should have something quality and reliable. If you want to discuss the protection of web applications on Node.js -
send me a message on Twitter.
The author of the publication reports that on August 7, RisingStack representatives contacted him. They reported that passwords were no longer stored in
plain text in their study guides. Now they use bcrypt in code and manuals.
In addition, he, inspired by the responses to his material, created
this document , in which he intends to collect all the best from the field of authentication in Node.js.
Dear readers! What can you say about the organization of the authentication system in web applications based on Node.js?