← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

bcrypt

security CWE-327 OWASP A2:2021 PHP 5.5+ Intermediate
debt(d5/e2/b3/t6)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches' (d5), semgrep/phpstan rules can flag md5/sha1 in auth contexts, but the 72-byte truncation and low cost factor are silent in production.

e2 Effort Remediation debt — work required to fix once spotted

Closest to 'one-line patch' (e1) with slight bump toward e3, swapping md5() for password_hash($pass, PASSWORD_BCRYPT) is mostly a one-line fix but may require DB column widening to 60 chars and rehashing on next login.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3), password hashing is concentrated in the auth component; the choice affects login/registration flows but doesn't shape the rest of the codebase.

t6 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7), the misconception that bcrypt is universally safe hides the 72-byte silent truncation — two long passwords differing past byte 72 hash identically, contradicting how developers expect a hash to behave.

About DEBT scoring →

Also Known As

blowfish password hash bcrypt hashing

TL;DR

A deliberately slow password hashing algorithm designed to resist brute-force attacks by tunable computational cost.

Explanation

bcrypt incorporates a cost factor (work factor) that determines how many iterations the algorithm runs. Increasing the cost doubles the computation time per hash — making brute-force attacks proportionally harder as hardware improves. A cost of 12 is a reasonable 2026 default. bcrypt also salts automatically, so two hashes of the same password are always different. In PHP, use password_hash($pass, PASSWORD_BCRYPT, ['cost' => 12]) and password_verify() to check.

Diagram

flowchart TD
    PASSWORD[Plain text password] --> SALT[Generate random salt<br/>22 chars base64]
    SALT --> COST[Apply cost factor<br/>default 12 = 2^12 iterations]
    COST --> HASH2[bcrypt hash<br/>60 chars total]
    HASH2 --> STORE2[(Store in DB)]
    subgraph Verify
        INPUT[Login attempt] --> VER[password_verify<br/>extracts salt + cost<br/>rehashes and compares]
        VER -->|match| OK[Authenticated]
        VER -->|no match| FAIL2[Rejected]
    end
    subgraph Cost_Factor
        C10[Cost 10 = 100ms]
        C12[Cost 12 = 400ms]
        C14[Cost 14 = 1600ms]
        C10 --> C12 --> C14
    end
style HASH2 fill:#6e40c9,color:#fff
style OK fill:#238636,color:#fff
style FAIL2 fill:#f85149,color:#fff
style C12 fill:#238636,color:#fff

Common Misconception

Bcrypt is always the best password hashing choice. Bcrypt silently truncates passwords at 72 bytes — two passwords differing only after character 72 hash identically. Argon2id is the modern recommendation.

Why It Matters

Bcrypt is purpose-built for passwords — it is intentionally slow and includes a salt by design, making rainbow tables and brute-force attacks computationally expensive. MD5 and SHA hashes are designed to be fast, which makes them catastrophically wrong for passwords.

Common Mistakes

  • Using MD5, SHA-1, or SHA-256 for passwords — these are fast hashes, not password hashing algorithms.
  • Setting the cost factor too low (below 10) — higher cost dramatically increases brute-force time.
  • Storing bcrypt hashes in columns shorter than 60 characters — the full hash will be truncated.
  • Hashing passwords before bcrypt (e.g. md5($pass)) — pre-hashing can reduce the effective input space.

Code Examples

✗ Vulnerable
// NEVER hash passwords with md5, sha1, sha256 — they're fast (crackable)
$hash = md5($password);
$hash = sha256($password);
$hash = sha256($salt . $password);
✓ Fixed
// PHP built-in — uses bcrypt by default (cost=10), upgradeable
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);

// Verify (timing-safe)
if (password_verify($input, $hash)) { /* authenticated */ }

// Check if rehash needed (after cost increase)
if (password_needs_rehash($hash, PASSWORD_BCRYPT, ['cost' => 12])) {
    $hash = password_hash($input, PASSWORD_BCRYPT, ['cost' => 12]);
    // save new hash
}

Added 13 Mar 2026
Edited 22 Mar 2026
Views 35
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 1 ping W 2 pings T 0 pings F 2 pings S 1 ping S 0 pings M 0 pings T 2 pings W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S
No pings yesterday
Perplexity 10 Amazonbot 8 Google 5 Ahrefs 2 Unknown AI 2 SEMrush 2 ChatGPT 2
crawler 27 crawler_json 4
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Low
⚡ Quick Fix
Use password_hash($pass, PASSWORD_BCRYPT) to hash, password_verify($input, $hash) to check — never md5/sha1
📦 Applies To
PHP 5.5+ web cli
🔗 Prerequisites
🔍 Detection Hints
md5($password or sha1($password or sha256($password in auth context
Auto-detectable: ✓ Yes phpstan semgrep
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Low ✓ Auto-fixable Fix: Low Context: Line Tests: Update
CWE-916

✓ schema.org compliant