Hashing passwords with NodeJS and MongoDB: bcrypt
Every time we propose a new project, there are recurring requirements, such as modularity, security, internationalization… Associated with the requirement of security, as well as data confidentiality, is the concealment of information in the database (so that it cannot be read by anyone who has access to it).
Sometimes it is enough to encrypt / hash users’ passwords, other times it is necessary to encrypt much more information. Unfortunately, however, in many projects this is still neglected and passwords in the database are kept clear. In API implementations, saving a password hash must be complemented with authentication systems that maintain security during access to all resources, for example, by authenticating API methods with JWT plus refresh token.
NodeJS and MongoDB
As part of our technology stack, most of our projects use the MEAN stack (MongoDB, Express, AngularJS and NodeJS). When we considered how to deal with password encryption and saved in MongoDB, since we used Mongoose in that project, one of the possibilities we considered was to use the mongoose-encryption plugin.
mongoose-encryption is very powerful, allowing options such as encryption, decryption, signing and authentication. You can choose whether to encrypt all the information in a collection of documents (except the _id), or to encrypt only a few fields, etc. Being a plugin about Mongoose, it allowed us to get rid of this problem and we delegated it completely to the plugin. But we decided not to use it, mainly because we didn’t want something that would make us dependent on Mongoose, since in other projects we wouldn’t use it and the solution wouldn’t work. Besides, we didn’t need to decrypt the password, it was enough for the software to be able to tell us if the password entered is equivalent to the one registered, so the plugin was giving us much more functionality than we needed. And the fact that a password can be decrypted makes the system a little more vulnerable. The ideal is to save a hash, so that from the value field saved in the database the password cannot be obtained again in clear.
Bcrypt is a password hashing function designed by Niels Provos and David Maxieres, based on Blowfish encryption. It is used by default on OpenBSD systems and some Linux and SUSE distributions. It has a built-in value called salt, which is a random fragment that will be used to generate the hash associated with the password, and will be saved with it in the database. This prevents two identical passwords from generating the same hash and the problems that this entails, for example, brute force attack on all passwords in the system at once. Another related attack is the Rainbow table, which are tables of associations between texts and their associated hash, to avoid its calculation and speed up the search for the password. With the salt, a degree of complexity is added that prevents the hash associated with a password from being unique.
NodeJS, MongoDB and bcrypt
Finally we decided to use the encryption library: bcrypt. With this library we can generate the hash of any field. It allows us to choose the value of saltRounds, which gives us control over the cost of processing the data. The higher this number is, the longer it takes for the machine to calculate the hash associated with the password. It is important when choosing this value, to select a number high enough that someone who tries to find the password for a user by brute force, requires so much time to generate all the possible hash of passwords that does not compensate him. And on the other hand, it must be small enough so as not to end the user’s patience when registering and logging in (this patience is not usually very high). By default, the saltRounds value is 10.
Let’s see a practical example of how the library works before jumping to the code. Let’s imagine that our password when we register is abc123. When creating the hash, we have passed to bcrypt a numerical saltRounds and with it has generated a random segment, for example, FvT4pO8HMbZX3ravxa8pEOVAenB. The library will also add some control parameters in front of it, to know which algorithm it is implemented with, and to know the salt complexity used, for example $2a$10$. Using the salt will encrypt the password (abc123) and result in another string, for example oXAUXEckEckEmHaHSuB8oNlvsLzR. With a separator character it will join the first part generated with the encrypted password, leaving something like this: $2a$10$FvT4pO8HMbZX3ravxa8pEOVAenB/oXAUXEckEckEmHaHSuB8oNlvsLzR
Let’s see an example of implementation.
In the user registration process, we have to apply the password hasheado before storing it in the database. To do this, we use the bcrypt library to calculate the hash associated with the password. The library will use the value we pass to it as saltRounds to apply the processing cost to generate the hash.
When we receive the registration request, we pass the password and the satlRounds value to the library for encryption, and with what it returns to us, we create the user in the database.
When a user logs into our system, we need to check that the password entered is correct. Unlike other systems that would decrypt the password in the database (if it is encrypted), and compare it with the one entered by the user, what we do with bcrypt is encrypt the one entered by the user. To do this, we will pass the password to bcrypt to calculate the hash, but also the password stored in the database associated with the user (hash). This is because, as mentioned before, the bcrypt algorithm used a random segment (salt) to generate the hash associated with the pasword. This was stored along with the password, and you need it to recalculate the hash of the password entered by the user and finally compare with the one entered when registering and see if they match.
The response of the call to the library will be a boolean that indicates whether the comparison is correct or not, and according to this value we will give the user for authenticated or not
When we receive a login request, we first look for the user in the database through the username, and delegate the password comparison to the bcrypt library, which will tell us if they are the same or not, and depending on the result you return we will proceed to return an authentication error or let them pass.
The bcrypt function, as a hashed-out algorithm, has important advantages over other encryption algorithms, such as SHA-256 with salt. The cost in time of calculation of the bcrypt algorithm is higher, so the passwords generated are much more secure against brute force attacks, as the attackers would need much more time to test each of the possible keys.