Hashing in Action: Understanding bcrypt (2024)

In previous posts to this Authentication Saga, we learned that storing passwords in plaintext must never be an option. Instead, we want to provide a one-way road to security by hashing passwords. However, we also explored that hashing alone is not sufficient to mitigate more involved attacks such as rainbow tables. A better way to store passwords is to add a salt to the hashing process: adding additional random data to the input of a hashing function that makes each password hash unique. The ideal authentication platform would integrate these two processes, hashing and salting, seamlessly.

There are plenty of cryptographic functions to choose from such as the SHA2 family and the SHA-3 family. However, one design problem with the SHA families is that they were designed to be computationally fast. How fast a cryptographic function can calculate a hash has an immediate and significant bearing on how safe the password is.

Faster calculations mean faster brute-force attacks, for example. Modern hardware in the form of CPUs and GPUs could compute millions, or even billions, of SHA-256 hashes per second against a stolen database. Instead of a fast function, we need a function that is slow at hashing passwords to bring attackers almost to a halt. We also want this function to be adaptive so that we can compensate for future faster hardware by being able to make the function run slower and slower over time.

At Auth0, the integrity and security of our data are one of our highest priorities. We use the industry-grade and battle-tested bcrypt algorithm to securely hash and salt passwords. bcrypt allows building a password security platform that can evolve alongside hardware technology to guard against the threats that the future may bring, such as attackers having the computing power to crack passwords twice as fast. Let's learn about the design and specifications that make bcrypt a cryptographic security standard.

Motivation Behind bcrypt

Technology changes fast. Increasing the speed and power of computers can benefit both the engineers trying to build software systems and the attackers trying to exploit them. Some cryptographic software is not designed to scale with computing power. As explained earlier, the safety of the password depends on how fast the selected cryptographic hashing function can calculate the password hash. A fast function would execute faster when running in much more powerful hardware.

To mitigate this attack vector, we could create a cryptographic hash function that can be tuned to run slower in newly available hardware; that is, the function scales with computing power. This is particularly important since, through this attack vector, people tend to keep the length of the passwords constant. Hence, in the design of a cryptographic solution for this problem, we must account for rapidly evolving hardware and constant password length.

This attack vector was well understood by cryptographers in the 90s and an algorithm by the name of bcrypt that met these design specifications was presented in 1999 at USENIX. Let's learn how bcrypt allows us to create strong password storage systems.

What is bcrypt?

bcrypt was designed by Niels Provos and David Mazières based on the Blowfish cipher>): b for Blowfish and crypt for the name of the hashing function used by the UNIX password system.

crypt is a great example of failure to adapt to technology changes. According to USENIX, in 1976, crypt could hash fewer than 4 passwords per second. Since attackers need to find the pre-image of a hash in order to invert it, this made the UNIX Team feel very comfortable about the strength of crypt. However, 20 years later, a fast computer with optimized software and hardware was capable of hashing 200,000 passwords per second using that function!

Inherently, an attacker could then carry out a complete dictionary attack with extreme efficiency. Thus, cryptography that was exponentially more difficult to break as hardware became faster was required in order to hinder the speed benefits that attackers could get from hardware.

The Blowfish cipher is a fast block cipher except when changing keys>), the parameters that establish the functional output of a cryptographic algorithm: each new key requires the pre-processing equivalent to encrypting about 4 kilobytes of text>), which is considered very slow compared to other block ciphers. This slow key changing is beneficial to password hashing methods such as bcrypt since the extra computational demand helps protect against dictionary and brute force attacks by slowing down the attack.

As shown in "Blowfish in practice">), bcrypt is able to mitigate those kinds of attacks by combining the expensive key setup phase of Blowfish with a variable number of iterations to increase the workload and duration of hash calculations. The largest benefit of bcrypt is that, over time, the iteration count can be increased to make it slower allowing bcrypt to scale with computing power. We can dimish any benefits attackers may get from faster hardware by increasing the number of iterations to make bcrypt slower.

"`bcrypt` was designed for password hashing hence it is a slow algorithm. This is good for password hashing as it reduces the number of passwords by second an attacker could hash when crafting a dictionary attack. "Tweet This

Another benefit of bcrypt is that it requires a salt by default. Let's take a deeper look at how this hashing function works!

"`bcrypt` forces you to follow security best practices as it requires a salt as part of the hashing process. Hashing combined with salts protects you against rainbow table attacks! Are password salts part of your security strategy?"Tweet This

How does bcrypt work?

Provos and Mazières, the designers of bcrypt, used the expensive key setup phase of the Blowfish cipher to develop a new key setup algorithm for Blowfish named "eksblowfish", which stands for "expensive key schedule Blowfish."

What's "key setup"? According to Ian Howson, a software engineer at NVIDIA: "Most ciphers consist of a key setup phase and an operation phase. During key setup, the internal state is initialised. During operation, input ciphertext or plaintext is encrypted or decrypted. Key setup only needs to be conducted once for each key that is used"bcrypt runs in two phases:

Phase 1:

A function called EksBlowfishSetup is setup using the desired cost, the salt, and the password to initialize the state of eksblowfish. Then, bcrypt spends a lot of time running an expensive key schedule which consists of performing a key derivation where we derive a set of subkeys from a primary key. Here, the password is used as the primary key. In case that the user selected a bad or short password, we stretch that password/key into a longer password/key. The aforementioned practice is also known as key stretching.

What we are going through this first phase is to promote key strengthening to slow down calculations which in turn also slow down attackers.

Phase 2:

The magic value is the 192-bit value OrpheanBeholderScryDoubt. This value is encrypted 64 times using eksblowfish in ECB mode>) with the state from the previous phase. The output of this phase is the cost and the 128-bit salt value concatenated with the result of the encryption loop.

Hashing in Action: Understanding bcrypt (3)

The resulting hash is prefixed with $2a$, $2y$, or $2b$. The prefixes are added to indicate usage of bcrypt and its version.

The result of bcrypt achieves core properties of a secure password function as defined by its designers:

  • It's preimage resistant.
  • The salt space is large enough to mitigate precomputation attacks, such as rainbow tables.
  • It has an adaptable cost.

The designers of bcrypt believe that the function will hold its strength and value for many years. Its mathematical design gives assurance to cryptographers about its resilience to attacks.

Regarding adaptable cost, we could say that bcrypt is an adaptive hash function as we are able to increase the number of iterations performed by the function based on a passed key factor, the cost. This adaptability is what allows us to compensate for increasing computer power, but it comes with an opportunity cost: speed or security?

bcrypt Best Practices

The challenge of security engineers is to decide what cost to set for the function. This cost is also known as the work factor. OWASP recommends as a common rule of thumb for work factor setting to tune the cost so that the function runs as slow as possible without affecting the users' experience and without increasing the need to use additional hardware that may be over budget.

Let's take a closer look at an example based on OWASP recommendations:

  • Perform UX research to find what are acceptable user wait times for registration and authentication.
  • If the accepted wait time is 1 second, tune the cost of bcrypt for it to run in 1 second on your hardware.
  • Analyze with your security team if the computation time is enough to mitigate and slow down attacks.

Users may be fine waiting for 1 or 2 seconds as they don't have to consistently authenticate. The process could still be perceived as quick. Whereas, this delay would frustrate the efforts of an attacker to quickly compute a rainbow table.

Being able to tune the cost of bcrypt allow us to scale with hardware optimization. Following a modern definition of Moore's Law, the number of transistors per square inch on integrated systems has been doubling approximately every 18 months. In 2 years, we could increase the cost factor to accommodate any change. However, we need to be careful with this: if we simply increase the work factor of bcrypt in our code, everyone will be locked out. A migration process is necessary in this case.

Check out a cool graph that shows the numbers of transistors on integrated circuit chips from 1971 to 2016.

"When using `bcrypt`, it's critical to find the right balance between security and usability. Increasing the cost factor increases computation time. Where do password operations happen? How long are your users willing to wait?"Tweet This

As an example on how the increasing the work factor increases the work time, I created a Node.js script that computed the hash of DFGh5546*%^__90 using a cost from 10 to 20.

const bcrypt = require("bcrypt");const plainTextPassword1 = "DFGh5546*%^__90";for (let saltRounds = 10; saltRounds < 21; saltRounds++) { console.time(`bcrypt | cost: ${saltRounds}, time to hash`); bcrypt.hashSync(plainTextPassword1, saltRounds); console.timeEnd(`bcrypt | cost: ${saltRounds}, time to hash`);}

In the next section, we are going to explore the Node.js implementation in more detail.The script was run on a 2017 MacBook Pro with the following specs (We get nice equipment at Auth0! Join us!):

  • Processor: 2.8 GHz Intel Core i7
  • Memory: 16 GB 2133 MHz LPDDR3
  • Graphics: Radeon Pro 555 2048 MB, Intel HD Graphics 630 1536 MB

These are the results:

bcrypt | cost: 10, time to hash: 65.683msbcrypt | cost: 11, time to hash: 129.227msbcrypt | cost: 12, time to hash: 254.624msbcrypt | cost: 13, time to hash: 511.969msbcrypt | cost: 14, time to hash: 1015.073msbcrypt | cost: 15, time to hash: 2043.034msbcrypt | cost: 16, time to hash: 4088.721msbcrypt | cost: 17, time to hash: 8162.788msbcrypt | cost: 18, time to hash: 16315.459msbcrypt | cost: 19, time to hash: 32682.622msbcrypt | cost: 20, time to hash: 66779.182ms

Plotting this data in Wolfram Alpha to create a least-squares fit graph, we observe that the time to hash a password grows exponentially as the cost is increased in this particular hardware configuration:

Hashing in Action: Understanding bcrypt (5)

For this data set, Wolfram Alpha gives us the following least-squares best fit equation:

28.3722 e^(0.705681x)

If we wanted to predict how long would it take to hash a password in this system when the cost is 30, we could simply plug that value for x:

28.3722 e^(0.705681(30)) = 44370461014.7

A cost factor of 30 could take 44370461014.7 milliseconds to calculate. That is, 739507.68 minutes or 513.55 days! A much faster machine optimized with the latest and the greatest technology available today could have smaller computation times. However, bcrypt can easily scale our hashing process to accommodate to faster hardware, leaving us a lot of wiggle room to prevent attackers from benefiting from future technology improvements.

If a company ever detects or suspects that a data breach has compromised passwords, even in hash form, it must prompt its users to change their password right away. While hashing and salting prevent a brute-force attack of billions of attempts to be successful, a single password crack is computationally feasible. An attacker may, with tremendous amount of computational power, or by sheer luck, crack a single password, but even then, the process would be most certainly slow due to the characteristics of bcrypt, giving the company and their users precious time to change passwords.

Now that we understand how bcrypt works, let's explore how it can be implemented in a web application at a high level.

Implementing bcrypt

We are going to explore its implementation using Node.js and its popular node.bcrypt.js implementation. You don't need to create a Node.js project. The purpose of this section is to show the common steps that developers have to take when integrating bcrypt in their backend.

node.bcrypt.js is installed via npm, a Node.js package manager via the following command:

npm install bcrypt

Then, on an entry-point file for the server, such as app.js, we create a set of variables to refer throughout the implementation:

// app.jsconst bcrypt = require("bcrypt");const saltRounds = 10;const plainTextPassword1 = "DFGh5546*%^__90";

bcrypt gives us access to a Node.js library that has utility methods to facilitate the hashing process. saltRounds represent the cost or work factor. We are going to use a random password, plainTextPassword1, for the example.

This Node.js implementation is interesting because it gives us two different techniques to hash the passwords. Let's explore them.

Technique 1: Generate a salt and hash on separate function calls.

// app.jsconst bcrypt = require("bcrypt");const saltRounds = 10;const plainTextPassword1 = "DFGh5546*%^__90";bcrypt .genSalt(saltRounds) .then(salt => { console.log(`Salt: ${salt}`); return bcrypt.hash(plainTextPassword1, salt); }) .then(hash => { console.log(`Hash: ${hash}`); // Store hash in your password DB. }) .catch(err => console.error(err.message));

Using the Promise pattern to control the asynchronous nature of JavaScript, in this technique, we first create a salt through the bcrypt.genSalt function that takes the cost, saltRounds. Upon success, we get a salt value that we then pass to bcrypt.hash along with the password, plainTextPassword1, that we want to hash. The success of bcrypt.hash provides us with the hash that we need to store in our database. In a full implementation, we would also want to store a username along with the hash in this final step.

Notice that I included some console.log statements to show the values of the salt and the hash as the process went along. Something that is really helpful in this implementation is that you do not have to create the salt yourself. The library creates a strong salt for you.

In the first run, I got the following results in the command line:

Salt: $2b$10$//DXiVVE59p7G5k/4Klx/eHash: $2b$10$//DXiVVE59p7G5k/4Klx/ezF7BI42QZKmoOD0NDvUuqxRE5bFFBLy

You won't be able to reproduce these results again since the salt is completely random every time genSalt is run. Running it again, I got the following output:

Salt: $2b$10$3euPcmQFCiblsZeEu5s7p.Hash: $2b$10$3euPcmQFCiblsZeEu5s7p.9OVHgeHWFDk9nhMqZ0m/3pd/lhwZgES

Hence, each password that we hash is going to have a unique salt and a unique hash. As we learned before, this helps us mitigate greatly rainbow table attacks.

Technique 2: Auto-generate a salt and a hash

In this version, we use a single function to both create the salt and hash the password:

// app.jsconst bcrypt = require("bcrypt");const saltRounds = 10;const plainTextPassword1 = "DFGh5546*%^__90";bcrypt .hash(plainTextPassword1, saltRounds) .then(hash => { console.log(`Hash: ${hash}`); // Store hash in your password DB. }) .catch(err => console.error(err.message));

This technique has a smaller footprint and may be easier to test. Again, a new hash is created each time the function is run, regardless of the password being the same.

Notice how in both techniques we are storing the hash and not the password. The user's password itself should not be stored anywhere in plaintext.

Once we have our password hashes stored in the database, how do we validate a user login? Let's check that out.

Validating a Password with a Hash

Using the bcrypt.hash method, let's see how we can compare a provided password with a stored hash. Since we are not connecting to a database in this example, we are going to create the hash and save it somewhere, like a text editor. The hash I got is:

$2b$10$69SrwAoAUNC5F.gtLEvrNON6VQ5EX89vNqLEqU655Oy9PeT/HRM/a.

Next, we are going to check the passwords and see how they match. First, we check if our stored hash matches the hash of the provided password:

// app.jsconst bcrypt = require("bcrypt");const plainTextPassword1 = "DFGh5546*%^__90";const hash = "$2b$10$69SrwAoAUNC5F.gtLEvrNON6VQ5EX89vNqLEqU655Oy9PeT/HRM/a";bcrypt .compare(plainTextPassword1, hash) .then(res => { console.log(res); }) .catch(err => console.error(err.message));

In this case, res is true, indicating that the password provided, when hashed, matched the stored hash.

Opposite, we expect to get false for res if we check the hash against plainTextPassword2:

// app.jsconst bcrypt = require("bcrypt");const plainTextPassword1 = "DFGh5546*%^__90";const plainTextPassword2 = "456kin&*jhjUHJ1";const hash = "$2b$10$69SrwAoAUNC5F.gtLEvrNON6VQ5EX89vNqLEqU655Oy9PeT/HRM/a";bcrypt .compare(plainTextPassword2, hash) .then(res => { console.log(res); }) .catch(err => console.error(err.message));

And, effectively, res is false. We did not store the salt though, so how does bcrypt.compare know which salt to use? Looking at a previous hash/salt result, notice how the hash is the salt with the hash appended to it:

Salt: $2b$10$3euPcmQFCiblsZeEu5s7p.Hash: $2b$10$3euPcmQFCiblsZeEu5s7p.9OVHgeHWFDk9nhMqZ0m/3pd/lhwZgES

bcrypt.compare deduces the salt from the hash and is able to then hash the provided password correctly for comparison.

That's the flow of using bcrypt in Node.js. This example is very trivial and there are a lot of others things to care about such as storing username, ensuring the whole backend application is secure, doing security tests to find vulnerabilities. Hashing a password, though essential, is just a small part of a sound security strategy.

Other languages would follow a similar workflow:

Simplifying Password Management with Auth0

The main idea of password verification is to compare two hashes and determine if they match each other. The process is very complex. A solid identity strategy demands an organization to keep current with cryptographic advances, design a process to phase out deprecated or vulnerable algorithms, provide pen testing, invest in physical and network security among many others. With all factors considered, it isn't easy or inexpensive.

You can minimize the overhead of hashing, salting and password management through Auth0. We solve the most complex identity use cases with an extensible and easy to integrate platform that secures billions of logins every month.

Auth0 helps you prevent critical identity data from falling into the wrong hands. We never store passwords in cleartext. Passwords are always hashed and salted using bcrypt. We've built state-of-the-art security into our product, to protect your business and your users.

Make the internet safer, sign up for a free Auth0 account today.

Hashing in Action: Understanding bcrypt (2024)

FAQs

Hashing in Action: Understanding bcrypt? ›

Unlike some other password-hashing algorithms that just hash the plain password, bcrypt uses the concept of salt. This unique randomly generated string provides an additional level of security for a generated hash. Before the plain password is hashed, a salt is generated.

What hashing method does bcrypt use? ›

Unlike some other password-hashing algorithms that just hash the plain password, bcrypt uses the concept of salt. This unique randomly generated string provides an additional level of security for a generated hash. Before the plain password is hashed, a salt is generated.

How does bcrypt work? ›

BCrypt Algorithm is used to hash and salt passwords securely. BCrypt permits building a password security stage that can advance nearby hardware innovation to guard against dangers or threats in the long run, like attackers having the computing power to guess passwords twice as quickly.

What is the difference between bcrypt and hash? ›

Bcrypt is a hashing algorithm that transforms a plain text password into a fixed-length string of characters, called a hash. Hashing is a one-way process, meaning that it is easy to generate a hash from a password, but hard to recover the password from the hash.

What is the difference between sha256 and bcrypt? ›

The technology in the Bcrypt algorithm and process limits attacks and makes it harder for attackers to compromise passwords. Bcrypt was not designed for encrypting large amounts of data. It is best implemented for passwords, however SHA-256 is better for large amounts of data because it is less costly and faster.

What is the difference between encrypt and bcrypt? ›

bcrypt() is for creating a Hash , which is a one-way process to turn a plain-text string into a hashed value. You cannot un-hash a value, so there is no way to return the value to it's "normal" state. encrypt() is for "obfuscation", which changes the plain-text string into a non-human readable value.

Is bcrypt hashing secure? ›

Bcrypt is an algorithm designed to hash and salt passwords for safe storage. It's an industry standard that's time-tested and proven to resist threats from hackers and other malicious agents.

What is the disadvantage of bcrypt? ›

Bcrypt is slower and requires some memory (4 kiB IIRC), so one spends 100ms to check a valid password whereas an attacker needs days / years to crack it because he's slowed down and can't use GPUs efficiently.

What is the advantage of using bcrypt? ›

The largest benefit of bcrypt is that, over time, the iteration count can be increased to make it slower allowing bcrypt to scale with computing power. We can dimish any benefits attackers may get from faster hardware by increasing the number of iterations to make bcrypt slower.

Can bcrypt passwords be cracked? ›

Bcrypt is a 184-bit hash created in 1999. It uses a salt to guard against rainbow table attacks and is adaptive. Over time, it becomes resistant to brute-force search attacks even with increasing computational power.

Is BCrypt easy to crack? ›

bcrypt is a very hard to crack hashing type, because of the design of this slow hash type that makes it memory hard and GPU-unfriendly (especially with high cost factors).

Can BCrypt be decrypted? ›

The algorithm does not support decryption.

How many characters is a BCrypt hash? ›

BCrypt hashed passwords and secrets have a 72 character limit. This is a limitation of the BCrypt algorithm and the Golang BCrypt library.

Why is bcrypt more secure? ›

Besides incorporating a salt to protect against rainbow table attacks, bcrypt is an adaptive function: over time, the iteration count can be increased to make it slower, so it remains resistant to brute-force search attacks even with increasing computation power.

Is bcrypt a one way hash? ›

Bcrypt uses adaptive hash algorithm to store password which is a one-way hash of the password.

Does bcrypt generate the same hash? ›

Everytime you use the same pwd1 & salt1 combination, you will get the same hash ((if you are using the same number of rounds everytime, which you should). bcrypt is a password hashing function.

Does bcrypt use AES? ›

After applying Bcrypt hashing, we use AES encryption algorithm.

Is bcrypt symmetric or asymmetric? ›

Bcrypt uses Blowfish symmetric-key block cipher and accepts 3 parameters; cost, salt, and password.

What is better than bcrypt? ›

scrypt (with a great enough work factor) has the added benefit of having extra RAM/Memory requirements (not just CPU), making it more GPU-resistant than SHA, BCrypt or PBKDF2.

Which hashing algorithm is safest? ›

To the time of writing, SHA-256 is still the most secure hashing algorithm out there. It has never been reverse engineered and is used by many software organizations and institutions, including the U.S. government, to protect sensitive information.

What is the best password hashing algorithm? ›

To protect passwords, experts suggest using a strong and slow hashing algorithm like Argon2 or Bcrypt, combined with salt (or even better, with salt and pepper). (Basically, avoid faster algorithms for this usage.) To verify file signatures and certificates, SHA-256 is among your best hashing algorithm choices.

What is the difference between bcrypt and JWT? ›

Bcrypt: A function that uses an algorithm to hash passwords. This is important for user security because if someone were to gain access to your database and the passwords are not hashed the users credentials are compromised. JWT: JWT stands for JSON Web Token. It is a standard for authentication in applications.

Is bcrypt better than MD5? ›

The main difference with regular digest algorithms such as MD5 or SHA256 is that the bcrypt algorithm is specifically designed to be CPU intensive in order to protect against brute force attacks. The exact complexity of the algorithm is configurable via the log_rounds parameter.

What is the difference between bcrypt and Blowfish? ›

Since the password in bcrypt is used as part of the encryption key, THAT is the property making it a one-way function. Blowfish is reversible in the sense that if you know the key you can reverse the encryption.

Is bcrypt more secure than sha256? ›

In regard to hashing passwords, bcrypt is a more safe option than SHA-256. To thwart dictionary and rainbow table attacks, it includes a salt (a random value). Furthermore, because bcrypt is intended to be slow, brute-force attacks are much harder to execute.

What is the maximum password length in bcrypt? ›

bcrypt has a maximum length input length of 72 bytes for most implementations. To protect against this issue, a maximum password length of 72 bytes (or less if the implementation in use has smaller limits) should be enforced when using bcrypt.

Can you reverse a hashed password? ›

You can't "reverse" password hashes. You can't "unhash" or "dehash" passwords. You can't "reverse" or "invert" MD5, SHA256, bcrypt, SHA1, or similar hashes, salted or unsalted. You (usually) can't "decode" passwords, "decrypt" password hashes or "reverse" or "unscramble" password hashes at all.

Should I use crypto or bcrypt? ›

Save this answer. Show activity on this post. Use bcrypt where you want to do slow and computationally expensive hashing -- this will generally be for hashes where you really don't want an attacker to be able to reverse the hash, e.g. user passwords. Use native crypto for everything else.

How many rounds are there in bcrypt? ›

A bcrypt cost of 6 means 64 rounds (26 = 64).

What is the structure of a bcrypt hash? ›

Bcrypt hashes have the format $2a$rounds$saltchecksum , where: rounds is a cost parameter, encoded as 2 zero-padded decimal digits, which determines the number of iterations used via iterations=2**rounds (rounds is 12 in the example).

What is the difference between MD5 and bcrypt? ›

The main difference with regular digest algorithms such as MD5 or SHA256 is that the bcrypt algorithm is specifically designed to be CPU intensive in order to protect against brute force attacks. The exact complexity of the algorithm is configurable via the log_rounds parameter.

Top Articles
Latest Posts
Article information

Author: Terrell Hackett

Last Updated:

Views: 6062

Rating: 4.1 / 5 (52 voted)

Reviews: 91% of readers found this page helpful

Author information

Name: Terrell Hackett

Birthday: 1992-03-17

Address: Suite 453 459 Gibson Squares, East Adriane, AK 71925-5692

Phone: +21811810803470

Job: Chief Representative

Hobby: Board games, Rock climbing, Ghost hunting, Origami, Kabaddi, Mushroom hunting, Gaming

Introduction: My name is Terrell Hackett, I am a gleaming, brainy, courageous, helpful, healthy, cooperative, graceful person who loves writing and wants to share my knowledge and understanding with you.