DEV Community

Muhammad Ilyasa Fadhlih
Muhammad Ilyasa Fadhlih

Posted on

How does BCrypt Verification work?

Have you ever wondered how can hashing algorithm verifies?

Before we going into that, we have to know that our password that we used to enter on a website, is going through one-way hashing function, thanks to it, your password is only known by ourself, even the server's owner shouldn't know about it.

Differs from encryption which is designed to be able to decrypt the contents, Hashing is only one way, that mean you hash it, you can't no longer retrieve it's original content. How does that work?

How does hashing work?

Before we learn about Bcrypt hashing and verification, I will try to explain to you a simple hashing concepts.

  1. Let's say you have a password, your password is pass123
  2. Each letter will be then transform into another letter using some algorithms. For example pass123, p transform to m, a transform to c and so on. But it's only one way, if p transform to m, doesn't mean that m will transform into p

Your password: pass123 might transform into D32rASDF and will always transfrom into D32rASDF everytime you enter pass123. So in practice, you can compare it, like these:


if (hash(input_password) === hash(stored_password_in_database)) {
  // user login succeed
}

Enter fullscreen mode Exit fullscreen mode

WAIT A MINUTE!, But in advance hashing algorithm like BCrypt, Argon2 and any other strong hashing algorithm will produce different hash result, even if you pass the same password.


bcrypt("pass123")
// result: $2a$12$G/bzOjiOAeFTHhvvImkRMeXQ9IiMK6Z38wcMs4H1W0wQ.ry7Loc1a

bcrypt("pass123")
// result: $2a$12$e1sitHZPq0KFuJ.G9dOmn.wKtfEwVp5O8qElZ.xzqOmQ5ZmA6BYm6

Enter fullscreen mode Exit fullscreen mode

Then, this will never work again.

if (bcrypt("pass123") === bcrypt("pass123)) {
  // user login always failed
}

Enter fullscreen mode Exit fullscreen mode

How does it verify then?

It actually pretty simple, instead of having your password passed straight into hash function and get result. It has another variable that stores random letters, this variable is called salt, this salt will help hashing algorithm makes more unpredictable result, they also has cost variable. Cost is the measure of the resources needed to calculate a hash.

According to Wikipedia, this is the structure of bcrypt:

$2<a/b/x/y>$[cost]$[22 character salt][31 character hash]
Enter fullscreen mode Exit fullscreen mode

For example, with input password abc123xyz, cost 12, and a random salt, the output of bcrypt is the string

$2a$12$R9h/cIPz0gi.URNNX3kh2OPST9/PgBkqquzi.Ss7KIUgO2t0jWMUW
\__/\/ \____________________/\_____________________________/
Alg Cost      Salt                        Hash
Enter fullscreen mode Exit fullscreen mode

Where:

  • $2a$: The hash algorithm identifier (bcrypt)
  • 12: Input cost (2^12 i.e. 4096 rounds)
  • R9h/cIPz0gi.URNNX3kh2O: A base-64 encoding of the input salt
  • PST9/PgBkqquzi.Ss7KIUgO2t0jWMUW: A base-64 encoding of the first 23 bytes of the computed 24 byte hash

As you can see from above, BCrypt stores the algorithm, cost, and salt alongside the hash result. You couldn't get bcrypt to work with only the hash.

Let me tell you this:

If you had the algorithm, cost, and salt, you could get the same hashing result, this mean you could compare it

Take a look at this code:

import bcrypt from "bcryptjs";

// your input
const inputPassword = "pass123";

// implement the function
function bcryptHash(input) {
    const cost = 12;
    const salt = bcrypt.genSaltSync(cost);
    const hash = bcrypt.hashSync(inputPassword, salt);

    return hash;
}

// generate hash by calling the function
// this result is then stored on the database
const result = bcryptHash(inputPassword);

// print to the console
console.log(result);
Enter fullscreen mode Exit fullscreen mode

If I execute the same file, it will of course generate 2 different hash

node index.js
# result = $2b$12$XfT3eq.O3t.NeklIEbzgKOfLZZgHSrYSBKNk5.f5IngnP/Z6/Xo/u
Enter fullscreen mode Exit fullscreen mode
node index.js
# result = $2b$12$FSmg5sXtB.XdJH2fFTrV7uBVKaiSdueu7FUcGSTXhoBnPssrjPvmC
Enter fullscreen mode Exit fullscreen mode

But, it has some similarities, it starts with $2b$12$, which the algorithm followed by the cost, then the 22 letter after is the salt

From the result, we now know that:

  1. Algorithm is start with 2b
  2. Cost is 12
  3. We need to the salt

// assumes this is the string of password that you get from the database
const storedPassword = "$2b$12$FSmg5sXtB.XdJH2fFTrV7uBVKaiSdueu7FUcGSTXhoBnPssrjPvmC";

// remove the prefix, then get the 22 letters of salt
const salt = storedPassword.replace("$2b$12$", "").substring(0, 22);

// salt = "FSmg5sXtB.XdJH2fFTrV7u"

Enter fullscreen mode Exit fullscreen mode

The salt is: FSmg5sXtB.XdJH2fFTrV7u

Now, instead of generating a new salt, you assign the salt that is extracted, and combines it with algorithm and the cost at the suffix


// this will give you fixed hash result
function bcryptFixedHash(input) {
    // const cost = 12;
    // const salt = bcrypt.genSaltSync(cost);

    // combines the alg, cost and salt together
    const salt = "$2b$12$" + "FSmg5sXtB.XdJH2fFTrV7u";

    const hash = bcrypt.hashSync(inputPassword, salt);

    return hash;
}

Enter fullscreen mode Exit fullscreen mode

const inputPassword = "pass123";

const storedPassword = 
"$2b$12$FSmg5sXtB.XdJH2fFTrV7uBVKaiSdueu7FUcGSTXhoBnPssrjPvmC";

const inputHash = bcryptFixedHash(inputPassword);

// now you get the exact same hash
// inputHash = $2b$12$FSmg5sXtB.XdJH2fFTrV7uBVKaiSdueu7FUcGSTXhoBnPssrjPvmC

Enter fullscreen mode Exit fullscreen mode

Comparing the hash

Now that you get the same hash, you could compare it to verify the user


if (storedPassword === inputHash) {
    // user login succeed
}

Enter fullscreen mode Exit fullscreen mode

In addition, you could use safe compare equal rather than === to compare, but that wouldn't be neccesary since the bcrypt hash result is always different and thus irrelevant to timing attack or similar.

Thank you!

Hot sauce if you're wrong - web dev trivia for staff engineers

Hot sauce if you're wrong ยท web dev trivia for staff engineers (Chris vs Jeremy, Leet Heat S1.E4)

  • Shipping Fast: Test your knowledge of deployment strategies and techniques
  • Authentication: Prove you know your OAuth from your JWT
  • CSS: Demonstrate your styling expertise under pressure
  • Acronyms: Decode the alphabet soup of web development
  • Accessibility: Show your commitment to building for everyone

Contestants must answer rapid-fire questions across the full stack of modern web development. Get it right, earn points. Get it wrong? The spice level goes up!

Watch Video ๐ŸŒถ๏ธ๐Ÿ”ฅ

Top comments (0)

๐Ÿ‘‹ Kindness is contagious

If this post resonated with you, feel free to hit โค๏ธ or leave a quick comment to share your thoughts!

Okay