为何BCrypt哈希要存储版本与迭代次数信息?
Great question—this is a super common point of confusion when you first start working with BCrypt! Let’s break down why embedding those parameters directly in the hash is actually a thoughtful design choice, even though it might seem counterintuitive at first glance.
Backward Compatibility & Operational Flexibility
Think about how businesses evolve over time: you might start with BCrypt version 2a and a cost factor of 10, but a few years later, as hardware gets faster, you’ll want to bump that cost up to 12 to keep brute-force attacks expensive. If you didn’t store the version and cost in the hash, you’d have two bad options:
- Maintain a separate database table mapping users to their hash’s configuration (a huge maintenance headache), or
- Break login for all existing users when you update your global BCrypt settings.
By including these parameters in the hash itself, each hash becomes a self-contained unit. When verifying a password, your code just parses the hash to get the exact version and cost used to generate it, then uses those values to hash the input password for comparison. No extra config tables, no broken logins when you upgrade settings.
Eliminating Costly Configuration Mistakes
Imagine this: your team updates the global BCrypt cost factor in your app’s config file, but someone typoes it as 15 instead of 12. If your hashes didn’t include the cost parameter, every user would suddenly fail to log in—total outage.
With the parameters embedded in the hash, verification doesn’t depend on external configs at all. The hash tells your code exactly what settings to use, so even if your global config is wrong, existing users can still log in normally. It’s a safety net against human error.
The Security Tradeoff Is Minimal (And Worth It)
You’re right that attackers can use the embedded parameters to configure their cracking tools—but let’s put this in perspective:
- BCrypt’s cost factor is intentionally designed to slow down hashing. A cost of 10 means 2^10 = 1024 iterations per hash, which is slow enough to make brute-force attacks impractical even with modern GPUs. Knowing the cost factor doesn’t eliminate this protection; it just means attackers don’t have to guess it.
- BCrypt has only a handful of widely used versions (2a, 2b, 2y) and common cost factors (10–14). Even if the parameters weren’t in the hash, attackers could easily enumerate all possible combinations in minutes. Hiding them gives almost no extra security, but adds a ton of operational overhead.
Standardization & Tooling Support
Nearly every mainstream BCrypt library (in Java, Python, Ruby, etc.) is built to generate and parse these parameter-included hashes. If you tried to strip out the parameters, you’d have to build custom parsing and verification logic from scratch—which is a great way to introduce security bugs. Sticking to the standard format means you get to leverage battle-tested, secure code without reinventing the wheel.
At the end of the day, embedding parameters in the hash aligns with a core best practice for password hashing: make each hash self-describing. Algorithms like Argon2 and PBKDF2 do the same thing for the exact same reasons—it’s about balancing security, usability, and operational sanity.
内容的提问来源于stack exchange,提问作者Pavlin Petkov

