Verify a Password Against a Bcrypt Hash

Learn how to verify a password against a stored bcrypt hash. Understand the constant-time comparison process, why you cannot decrypt a hash, and common verification pitfalls to avoid.

Implementation

Detailed Explanation

Verifying a Password Against a Bcrypt Hash

Password verification with bcrypt works by re-hashing the candidate password using the salt extracted from the stored hash, then comparing the result. You never "decrypt" a bcrypt hash — the process is one-way.

How Verification Works

  1. User submits their password (e.g., "mypassword123")
  2. Retrieve the stored hash from the database (e.g., "$2b$12$LJ3m4ys...")
  3. Extract the salt and cost factor from the stored hash
  4. Hash the candidate password using the extracted salt and cost factor
  5. Compare the newly computed hash with the stored hash
  6. If they match, the password is correct

Code Examples

Node.js:

const bcrypt = require('bcrypt');

// Hashing (during registration)
const hash = await bcrypt.hash('mypassword123', 12);
// Store hash in database

// Verification (during login)
const isMatch = await bcrypt.compare('mypassword123', hash);
// isMatch === true

Python:

import bcrypt

# Hashing
hashed = bcrypt.hashpw(b'mypassword123', bcrypt.gensalt(rounds=12))

# Verification
is_match = bcrypt.checkpw(b'mypassword123', hashed)
# is_match == True

Constant-Time Comparison

Bcrypt libraries use constant-time comparison to prevent timing attacks. A naive string comparison (hash1 === hash2) returns false as soon as it finds the first mismatched character, leaking information about how many characters matched. Constant-time comparison always takes the same amount of time regardless of where the mismatch occurs.

Common Pitfalls

  1. Comparing hashes directly — never compare two bcrypt hashes with ===. Always use the library’s compare function, which handles salt extraction and constant-time comparison
  2. Encoding issues — ensure the password is encoded consistently (UTF-8) during both hashing and verification
  3. Truncation — bcrypt silently truncates passwords beyond 72 bytes. If you hash a 100-byte password, only the first 72 bytes are used
  4. Null bytes — some implementations truncate at null bytes. Avoid accepting null bytes in passwords
  5. Async vs sync — in Node.js, prefer bcrypt.compare() (async) over bcrypt.compareSync() to avoid blocking the event loop

What Happens on Failure

When verification fails, the library simply returns false. It does not indicate whether the cost factor, salt, or hash portion mismatched — this is intentional to prevent information leakage.

Use Case

Password verification is the most common bcrypt operation in any application with user authentication. Understanding the verification process helps developers avoid subtle bugs like encoding mismatches, truncation issues, and timing vulnerabilities. It is especially important when migrating between bcrypt libraries or across programming languages, where differences in API design can introduce verification failures.

Try It — Bcrypt Generator

Open full tool