Bcrypt password generator
Bcrypt Password Hashing: Complete Guide
Bcrypt is a password hashing scheme designed specifically for securely storing passwords. Unlike general-purpose hash functions such as MD5 or SHA-256, Bcrypt is intentionally computationally expensive, making large-scale offline password guessing attacks more difficult.
What is Bcrypt used for?
Bcrypt is primarily used to store passwords securely. Instead of storing a user's plaintext password, an application stores a Bcrypt password hash and uses a password verification function to verify future login attempts.
What Is Bcrypt?
Bcrypt is a password hashing scheme based on the Blowfish block cipher and its intentionally expensive key schedule. It was designed by Niels Provos and David Mazières for password storage and was introduced in the context of OpenBSD.
The important characteristic of Bcrypt is that it is deliberately expensive to compute. Its work factor, also called the cost factor, can be increased as hardware becomes faster. This makes password guessing more expensive for an attacker while allowing legitimate applications to adjust the computational cost over time.
How Does Bcrypt Work?
Bcrypt combines a password, a cryptographically random salt, and a configurable cost factor to produce a password hash. The resulting Bcrypt string contains the information needed for verification, including the algorithm identifier, cost, salt, and derived password value.
This means an application does not need to store the salt or cost factor separately when using a standard Bcrypt password-hash format.
1. Password
2. Salt
3. Cost Factor
4. Password Hash
Bcrypt Password Hash Format Explained
A Bcrypt password hash is a structured string rather than simply a random sequence of characters. A common modern example is:
$2b$12$FPWWO2RJ3CK4FINTw0Hi8OiPKJcX653gzSS.jqltHFMxyDmmQ0Hqq The string contains a version identifier, a two-digit cost factor, a 128-bit salt, and the Bcrypt derived value. The salt and derived value use Bcrypt's own Radix-64-style encoding rather than standard RFC 4648 Base64.
| Part | Example | Meaning |
|---|---|---|
| Version | $2b$ | Bcrypt version/compatibility identifier. |
| Cost | 12 | Logarithmic work factor. The computational work is approximately proportional to 212. |
| Salt | FPWWO2RJ3CK4FINTw0Hi8O | 16-byte (128-bit) randomly generated salt, encoded into 22 characters. |
| Derived value | iPKJcX653gzSS.jqltHFMxyDmmQ0Hqq | The Bcrypt derived password value encoded into 31 characters. |
Typical Bcrypt String Length
Standard Bcrypt password hashes are normally 60 characters long. However, applications should not assume that every future password hashing algorithm will produce 60-character strings. When using PHP's PASSWORD_DEFAULT, for example, the database column should be large enough to accommodate future algorithms; PHP recommends allowing more than 60 characters, with 255 characters being a practical choice.
Why Does Bcrypt Have So Many Prefixes?
One of the most confusing aspects of Bcrypt is that the beginning of a password hash may be $2$, $2a$, $2b$, $2x$, or $2y$.
These prefixes are not a ranking from weak to strong. They are primarily version and compatibility identifiers introduced because the historical Bcrypt ecosystem contained specification changes and implementation-specific bugs.
In particular, $2x$ and $2y$ were introduced by the crypt_blowfish implementation after a bug involving 8-bit characters was discovered. They are not "stronger versions" of Bcrypt.
Prefixes Identify Compatibility Behavior
A Bcrypt prefix should be treated as part of the hash format. It tells the verification implementation which historical Bcrypt behavior should be used. Changing the prefix manually does not recalculate the password hash.
Bcrypt Prefix History at a Glance
| Prefix | Historical Context | Important Point | Modern Relevance |
|---|---|---|---|
$2$ | Original Bcrypt version identifier from the early OpenBSD implementation. | Historical/obsolete form. | Mostly relevant when dealing with very old legacy password databases. |
$2a$ | Became the commonly used Bcrypt version identifier after changes to the handling of password input, including non-ASCII input and the terminating NUL byte. | The prefix alone does not always establish whether a historical crypt_blowfish implementation was affected by the 2011 bug. | Very common in legacy Bcrypt databases and still supported by many libraries. |
$2x$ | Introduced by crypt_blowfish to explicitly identify hashes generated using the historical 8-bit character sign-extension bug. | Represents the buggy behavior for compatibility. | Only important when migrating or verifying affected legacy hashes. |
$2y$ | Introduced by crypt_blowfish to identify the corrected behavior after the 8-bit character handling bug was fixed. | It is not "stronger bcrypt"; it identifies the corrected implementation behavior. | Used by PHP's PASSWORD_BCRYPT. |
$2b$ | Introduced by OpenBSD after a separate bug involving password-length handling for inputs longer than 255 bytes. | It distinguishes the corrected OpenBSD behavior from older implementations. | Common modern Bcrypt identifier in many non-PHP implementations. |
The Important Relationship Between the Prefixes
| Situation | Prefix | What It Means |
|---|---|---|
| Very old original Bcrypt | $2$ | Original historical identifier. Obsolete for modern password storage. |
| Common legacy Bcrypt | $2a$ | Historical Bcrypt identifier. In some old crypt_blowfish databases, it may be ambiguous with respect to the 8-bit-character bug. |
| Known buggy crypt_blowfish behavior | $2x$ | Explicitly selects compatibility with the historical sign-extension bug. |
| Corrected crypt_blowfish behavior | $2y$ | Explicitly identifies the corrected crypt_blowfish behavior. |
| Corrected OpenBSD behavior | $2b$ | Modern OpenBSD Bcrypt identifier introduced after a separate long-password length-handling bug. |
$2x$ Is Not a "Weak Cost Factor"
The $2x$ prefix does not mean that the cost factor is too low. A hash such as $2x$12$... can still have a cost of 12.
The x identifies a specific historical implementation bug. The 12 identifies the computational cost. These are separate concepts.
Why Was $2x$ Created?
In 2011, a bug was discovered in the crypt_blowfish implementation of Bcrypt. The implementation incorrectly handled certain characters with the high bit set, such as some 8-bit/non-ASCII byte values.
The compatibility solution was to distinguish hashes produced using the broken behavior from hashes produced using the corrected behavior. The $2x$ identifier represents the old buggy behavior, while $2y$ represents the corrected behavior.
This is why you should never blindly replace every $2a$ prefix with $2x$. A $2a$ hash does not automatically mean that the buggy behavior was used.
Never Upgrade a Bcrypt Hash by Editing Its Prefix
Changing $2a$ to $2b$, $2x$, or $2y$ does not recompute the password hash. Prefix changes should only be performed when a specific implementation's compatibility requirements call for them. For password migrations, verify the user's password and generate a completely new hash using the current password hashing API.
What Is the Bcrypt Cost Factor?
The Bcrypt cost factor controls the computational work required by the algorithm. It is sometimes informally called the number of "rounds", but that description can be misleading.
The cost parameter is logarithmic. Increasing the cost by one approximately doubles the computational work. The work therefore scales approximately as 2cost.
Bcrypt Cost Examples
| Cost | Relative Work Scale | Compared With Previous Cost |
|---|---|---|
| 4 | 24 = 16 | Baseline example |
| 10 | 210 = 1,024 | ~64× cost 4 |
| 12 | 212 = 4,096 | ~4× cost 10 |
| 14 | 214 = 16,384 | ~4× cost 12 |
Is Bcrypt Cost 4 Secure?
A cost of 4 is very low and should not be used for production password storage. It may be useful for demonstrations, testing, or benchmarking. For legacy systems that continue to use Bcrypt, OWASP recommends a work factor of at least 10, while the actual production value should be determined by benchmarking the authentication server.
There is no universal cost value that is optimal for every server. A practical approach is to benchmark the production environment and select the highest cost that provides acceptable authentication latency while still leaving sufficient capacity for legitimate login traffic.
The cost should be reviewed periodically as hardware performance improves. Existing passwords can normally be migrated to a higher cost gradually during successful login operations.
What Is a Bcrypt Salt?
A salt is a random value generated for a password before the Bcrypt computation. Using a different salt for each password means that two users with the same password will normally have different Bcrypt hashes.
Does the Bcrypt Salt Need to Be Secret?
No. The salt is not a password or encryption key. It is normally stored together with the Bcrypt hash.
Its purpose is to prevent identical passwords from producing identical hashes and to make large-scale precomputed password attacks impractical. An attacker who has a password database still has to perform password guesses against the individual salts rather than simply reusing one precomputed table for all accounts.
Is Bcrypt Encryption or Hashing?
Bcrypt is a password hashing scheme, not an encryption algorithm. This distinction is important.
Encryption
Encrypted data can be decrypted when the correct cryptographic key is available.
Password Hashing
A password hash is designed for verification rather than recovering the original password.
Bcrypt 72-Byte Password Limit
Important Bcrypt Limitation
Traditional Bcrypt implementations have a maximum effective password input length of 72 bytes. This is a byte limit, not necessarily a 72-character limit.
This distinction matters when passwords contain Unicode characters. With UTF-8, a single character can occupy multiple bytes, so 72 characters can represent considerably more than 72 bytes.
Applications should understand the exact input behavior of the Bcrypt library they use and should avoid silently treating a password longer than the supported input length as though the entire password were being processed.
72 Bytes Is Not 72 Characters
| Password Type | Example | Why It Matters |
|---|---|---|
| ASCII | password123 | ASCII characters normally occupy one byte each in UTF-8. |
| Unicode | ÄäÖöÜüßẞ | Some characters occupy multiple UTF-8 bytes. |
| Very long password | ... | A password may contain fewer than 72 characters but still reach 72 bytes depending on its encoding. |
Bcrypt and NUL Bytes
Bcrypt has historical string-handling semantics involving the terminating NUL byte (\0). This becomes especially important when developers attempt to pre-hash passwords with a binary hash function and then feed the raw binary output into Bcrypt.
Do Not Naively Pre-Hash Binary Data
A common but potentially dangerous construction is:
bcrypt(SHA-256(password)) If the raw binary SHA-256 output contains a NUL byte, historical Bcrypt string processing can cause the input to be terminated early. This can create undesirable collisions or interoperability problems.
If a system has a specific requirement to pre-hash passwords before Bcrypt, the implementation should follow a documented, carefully reviewed construction that converts the intermediate value into a suitable printable representation and accounts for Bcrypt's input-length limitation. In most applications, simply using Argon2id or Bcrypt directly through a trusted password hashing API is preferable.
What Do $2a$, $2b$, $2x$ and $2y$ Mean?
You may encounter different Bcrypt prefixes when working with existing password databases. These identifiers are primarily related to historical implementations, compatibility, and bug fixes. They should not be interpreted as a simple ranking from "weak" to "strong".
| Prefix | Origin | Purpose | Bug / Change | Modern Interpretation |
|---|---|---|---|---|
$2$ | Original Bcrypt/OpenBSD history | Original version identifier | Historical format | Obsolete/legacy. Mostly relevant when migrating very old databases. |
$2a$ | OpenBSD / Bcrypt ecosystem | Common historical Bcrypt identifier | Became associated with the revised handling of password input. Later, a third-party implementation's bug made some old $2a$ hashes ambiguous. | Widely encountered in legacy databases. |
$2x$ | crypt_blowfish | Compatibility with buggy behavior | 8-bit character sign-extension bug. | Legacy only. Indicates the historical buggy behavior. |
$2y$ | crypt_blowfish | Identify corrected behavior | Corrected handling of the 8-bit character bug. | PHP's PASSWORD_BCRYPT uses this identifier. |
$2b$ | OpenBSD | Identify corrected OpenBSD behavior | Introduced after a separate password-length handling bug involving lengths above 255 bytes. | Common modern Bcrypt identifier outside PHP. |
Important Compatibility Detail
The $2x$ and $2y$ identifiers were not adopted as universal Bcrypt versions by the original OpenBSD project. They were introduced specifically in the crypt_blowfish ecosystem to distinguish buggy and corrected behavior.
OpenBSD later introduced $2b$ for its own separate bug fix. Modern compatible implementations may therefore accept multiple Bcrypt prefixes, but applications should rely on their specific password hashing library rather than manually converting prefixes.
Bcrypt Prefixes in PHP
PHP is particularly relevant to the $2x$ and $2y$ history because PHP's password hashing ecosystem used the crypt_blowfish implementation involved in the 2011 bug.
In modern PHP, PASSWORD_BCRYPT generates a standard crypt()-compatible Bcrypt hash using the $2y$ identifier.
$2y$12$... Therefore, if a PHP application generates a Bcrypt password with password_hash($password, PASSWORD_BCRYPT), seeing $2y$ is expected.
PHP's PASSWORD_DEFAULT currently uses Bcrypt, but that constant is intentionally designed to change in the future if PHP adopts a stronger default. Applications should therefore use a database column capable of storing hashes longer than the current 60-character Bcrypt output; PHP recommends a column capable of storing up to 255 characters.
Is Bcrypt Secure?
Bcrypt remains a well-established and widely supported password hashing algorithm. Its primary security benefit is that it makes offline password guessing substantially more expensive than using fast general-purpose hash functions.
However, Bcrypt does not make weak passwords unbreakable. If an attacker obtains a database of password hashes, they can attempt password guesses offline.
For new password-storage systems, Argon2id is generally preferred because it is memory-hard and provides better resistance against attackers who can use highly parallel hardware. Bcrypt remains particularly useful for existing systems, compatibility requirements, and platforms where Argon2id is unavailable.
Salt
Prevents identical passwords from producing identical hashes.
Cost
Makes each password guess more computationally expensive.
Bcrypt Password Hashing Best Practices
Password Storage Checklist
- Never store plaintext passwords in a database.
- Use a dedicated password hashing API instead of a general-purpose hash function.
- Use a cryptographically secure random salt or let the password hashing library generate it automatically.
- Never use a static salt shared by all passwords.
- Select a Bcrypt cost factor by benchmarking the production environment.
- For legacy Bcrypt systems, use a work factor of at least 10 and choose a higher value when production performance allows.
- Increase the cost factor over time as hardware becomes faster.
- Do not manually modify Bcrypt version prefixes.
- Be aware of Bcrypt's 72-byte password input limitation.
- Do not naively feed raw binary hashes into Bcrypt as a pre-hashing strategy.
- Use the framework's dedicated password verification function rather than implementing password verification manually.
- Rehash passwords after successful authentication when the stored parameters are outdated.
- Allow long passwords and passphrases while understanding the input limits of the password hashing algorithm actually used.
- Consider checking passwords against known breached-password lists where appropriate.
- Consider Argon2id when designing a new password storage system.
Password Length and Password Policy
Password hashing and password policy solve different problems. Bcrypt protects stored passwords against offline guessing, while password policy determines which passwords users are allowed to choose.
Modern password guidance generally favors allowing long passwords and passphrases instead of imposing arbitrary composition rules such as requiring a mixture of uppercase letters, lowercase letters, numbers, and special characters.
Applications should also consider blocking passwords that are known to have been compromised in previous data breaches or are otherwise extremely common.
Long Passwords and Bcrypt
There is an important distinction between allowing long passwords at the user interface level and actually processing the entire password with Bcrypt. Because Bcrypt has a 72-byte effective input limitation, applications using Bcrypt must understand how their chosen library handles passwords beyond that limit. New systems that require a password hashing algorithm without this Bcrypt-specific limitation should consider Argon2id.
Bcrypt vs Argon2id
Bcrypt is still useful, especially when compatibility and broad library support are important. For a new application, however, it is generally preferable to evaluate Argon2id, a modern memory-hard password hashing algorithm.
| Feature | Bcrypt | Argon2id |
|---|---|---|
| Password hashing | Yes | Yes |
| Adjustable computational cost | Yes | Yes |
| Memory-hard design | No | Yes |
| Bcrypt-style 72-byte input limitation | Yes | No |
| Suitable for new systems | Usually compatibility-driven | Generally preferred |
| Legacy compatibility | Excellent | Depends on platform |
Choosing a Password Hashing Algorithm
For new applications, consider modern password hashing algorithms such as Argon2id. Bcrypt remains a practical choice when maintaining an existing Bcrypt-based system, when compatibility is important, or when Argon2id is not available.
For legacy systems using Bcrypt, use a sufficiently high work factor and plan gradual rehashing as authentication occurs.
What Is a Password Pepper?
A pepper is an optional application-level secret that is combined with a password before or during password hashing. Unlike a salt, a pepper is intended to remain secret and should be stored separately from the password database, for example in a dedicated secrets-management system.
A pepper can provide additional defense in depth if an attacker obtains only the password database. However, it does not replace a proper password hashing algorithm, a unique salt, or good password security practices.
Pepper Is Optional
Do not add a pepper to Bcrypt casually without understanding the application's recovery, rotation, and secret-management requirements. A lost pepper can prevent verification of every password that depends on it.
How to Use the Bcrypt Password Generator
The Bcrypt Password Generator above can be used to generate Bcrypt password hashes for development, testing, and educational purposes. Enter a test password, choose a cost factor, and generate the resulting Bcrypt hash.
Do Not Enter Real Passwords
Never enter a real account password, reused password, or production credential into an online password generator.
For production applications, password hashes should be generated inside the application or server using the trusted password hashing API provided by the programming language or framework.
How to Verify a Bcrypt Password
Applications should not decrypt or reverse a Bcrypt hash because Bcrypt is not an encryption algorithm. During login, the application takes the password supplied by the user, reads the parameters stored in the Bcrypt hash, performs the appropriate Bcrypt computation, and uses the password verification API to determine whether the password matches.
Do not implement password verification by manually manipulating the Bcrypt string or by replacing its prefix. Use the password verification function supplied by the language or framework.
User Password
Bcrypt Verification
Password Valid / Invalid
PHP Bcrypt Verification Example
PHP provides dedicated APIs for password hashing and verification. A typical Bcrypt workflow is to generate the hash with password_hash(), verify it with password_verify(), and check whether it needs to be regenerated with password_needs_rehash().
<?php $password = $_POST['password']; $hash = password_hash($password, PASSWORD_BCRYPT, [ 'cost' => 12 ]); if (password_verify($password, $hash)) { echo 'Password is valid'; } ?> PHP's password_verify() is designed specifically for password verification and is safe against timing attacks. Applications should prefer it over implementing their own comparison logic.
Bcrypt Password Rehashing
Password hashing parameters may need to be strengthened over time. A common migration strategy is to verify the user's existing password during login and, when the password is successfully authenticated using an older configuration, generate a new hash using the current recommended parameters.
User Logs In
Verify Existing Hash
Check Password Parameters
Store the New Hash
PHP Rehashing Example
<?php $storedHash = $user['password_hash']; $password = $_POST['password']; $options = [ 'cost' => 12 ]; if (password_verify($password, $storedHash)) { if (password_needs_rehash( $storedHash, PASSWORD_BCRYPT, $options )) { $newHash = password_hash( $password, PASSWORD_BCRYPT, $options ); // Store $newHash in the database. } // Login successful. } ?> Rehash Only After Successful Authentication
The plaintext password should only be used to generate the replacement hash after the supplied password has successfully verified against the existing hash. The application should never attempt to "upgrade" an unknown password hash without knowing the user's actual password.
Migrating Legacy Bcrypt Passwords
Legacy databases may contain $2$, $2a$, $2x$, $2y$, or $2b$ hashes depending on which implementation and version generated them.
The safest migration strategy is generally verify first, then rehash. The application should identify which legacy formats its password library supports and use the library's documented compatibility behavior.
| Legacy Situation | Recommended Approach |
|---|---|
| Old Bcrypt with low cost | Verify the password and rehash using a stronger current cost after successful login. |
$2a$ legacy hash | Let the chosen password library handle compatibility. Do not blindly change the prefix. |
Known $2x$ hash | Use an implementation that explicitly supports the historical buggy behavior, verify the password, then immediately generate a corrected modern hash. |
| Old Bcrypt database from another language | Test interoperability using known test vectors before deploying the migration. |
| Unknown legacy password algorithm | Implement a controlled migration path that first verifies using the legacy algorithm and then immediately replaces the hash with the modern scheme. |
Do Not Assume All $2a$ Hashes Are the Same
The $2a$ identifier alone may not tell you whether a historical crypt_blowfish hash was generated before or after the 2011 8-bit-character bug fix. Legacy migration should therefore be based on the actual implementation history and documented compatibility behavior rather than simply replacing prefixes.
Bcrypt Prefix Comparison
| Prefix | Historical Source | 8-bit Character Bug? | Long-Length Bug? | Typical Use Today |
|---|---|---|---|---|
$2$ | Original Bcrypt | Historical format | Historical format | Legacy only |
$2a$ | OpenBSD/Bcrypt ecosystem | Prefix alone may be ambiguous for old crypt_blowfish hashes. | Depends on implementation/version | Legacy databases |
$2x$ | crypt_blowfish | Explicitly represents the buggy sign-extension behavior. | Not its defining issue | Legacy compatibility |
$2y$ | crypt_blowfish / PHP | Corrected behavior | Corrected crypt_blowfish behavior | PHP Bcrypt |
$2b$ | OpenBSD | Corrected behavior | Introduced after the 255-byte length overflow issue was fixed. | Modern Bcrypt implementations |
Bcrypt Security Limitations
Bcrypt is not a magic solution for password security. Its effectiveness depends on correct implementation, appropriate cost configuration, strong user passwords, and secure application architecture.
| Limitation | Impact | Recommended Response |
|---|---|---|
| 72-byte input limit | Passwords beyond the effective input limit may not be processed in full. | Understand the library behavior and consider Argon2id for new systems. |
| CPU-oriented design | Bcrypt is not memory-hard, so attackers can use highly parallel hardware more efficiently than against memory-hard password hashing schemes. | Prefer Argon2id for new systems where available. |
| Low cost factor | Makes offline password guessing much cheaper. | Benchmark and use an appropriately high cost. |
| Weak passwords | Even a slow password hash can eventually be guessed if the password is weak. | Encourage long passwords and block known compromised/common passwords. |
| Implementation differences | Historical prefixes and bugs can cause interoperability problems. | Use a well-maintained password hashing library and test legacy migration paths. |
Conclusion
Bcrypt was designed specifically for password storage and remains a widely supported password hashing option. Its use of a random salt and adjustable computational cost makes offline password guessing significantly more expensive than when using fast, general-purpose hash functions.
Bcrypt is not encryption, it does not make weak passwords impossible to guess, and it has important implementation details such as its 72-byte password input limitation. Applications should also understand the historical $2$, $2a$, $2x$, $2y$, and $2b$ identifiers when working with legacy password databases.
The different prefixes are primarily historical compatibility markers rather than a simple security ranking. In particular, $2x$ identifies the historical buggy crypt_blowfish behavior, while $2y$ identifies its corrected behavior. $2b$ was introduced by OpenBSD for a separate password-length handling bug.
For new applications, modern password hashing schemes such as Argon2id should generally be preferred. For existing applications, a properly configured Bcrypt implementation, a suitable cost factor, secure password verification, and gradual rehashing can provide a strong password storage strategy.
Key Takeaways
- Bcrypt is designed for password hashing, not encryption.
- Each password should use a cryptographically random salt.
- The salt does not need to be kept secret.
- The cost factor controls the computational expense of Bcrypt.
- Increasing the cost by one approximately doubles the computational work.
- Very low cost values such as
4should not be used for production passwords. - For legacy Bcrypt systems, OWASP recommends a work factor of at least
10. - Bcrypt traditionally has a 72-byte password input limitation.
- 72 bytes is not necessarily 72 characters when UTF-8 is involved.
- Be careful when pre-hashing passwords and feeding binary hash output into Bcrypt.
$2x$identifies historical buggy crypt_blowfish behavior.$2y$identifies corrected crypt_blowfish behavior and is used by PHP's Bcrypt API.$2b$is a later OpenBSD Bcrypt identifier associated with a separate length-handling bug fix.- Do not manually change Bcrypt version prefixes.
- Use a trusted password hashing and verification API in production.
- Rehash passwords after successful authentication when the stored parameters are outdated.
- Consider Argon2id for new password storage systems.
- Never enter real passwords into an online password generator.


