crypt
Hashes a string one-way using a salt that selects the algorithm, such as bcrypt or SHA-512.
crypt(string $string, string $salt): string- Added in PHP 4.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$string | string | The string to be hashed. Marked #[\SensitiveParameter]. CRYPT_BLOWFISH truncates it at 72 bytes. |
$salt | string | A salt string whose prefix selects the algorithm, for example "$2y$" for bcrypt or "$6$" for SHA-512. |
Return value
stringThe hashed string, or a string shorter than 13 characters that is guaranteed to differ from the salt, on failure.
Errors
Throws an ArgumentCountError if salt is omitted. An invalid salt yields a short failure string rather than an exception.
Changelog
- PHP 8.0.0The salt parameter is no longer optional. Omitting it is now a fatal ArgumentCountError instead of producing a hash with a weak random salt.
crypt() is the classic Unix one-way hashing function. The $salt argument does double duty: its prefix selects the algorithm and its remainder supplies the salt. $2y$ means bcrypt, $6$ means SHA-512, $5$ SHA-256, $1$ MD5-crypt, and a bare two-character salt means ancient DES.
When to reach for it
For new password storage, use password_hash() instead — it wraps crypt(), picks a modern algorithm, generates a cryptographically secure salt for you and lets password_needs_rehash() upgrade costs over time. Reach for crypt() directly only when you must produce or verify a specific external format: an Apache htpasswd file, an /etc/shadow entry, or a legacy database column whose hashes were made with a fixed scheme.
What trips people up
Verification is done by passing the stored hash itself as the salt: crypt($input, $stored) re-reads the algorithm, cost and salt out of the stored value and must reproduce it exactly. Compare the result with hash_equals(), never ===.
Since PHP 8.0 the salt is mandatory. An invalid or malformed salt does not throw — it returns a short failure string, so unchecked code can end up storing something that is not a hash at all. And bcrypt silently ignores everything past 72 bytes of the input.
Example
When to use it
- Verify a password against a legacy hash column created before password_hash() existed.
- Generate a bcrypt entry compatible with an Apache htpasswd file.
- Check a credential against a SHA-512 crypt hash copied from /etc/shadow.
Frequently asked questions
What is the difference between crypt and password_hash?
password_hash() calls crypt() internally but generates a secure random salt, picks a modern default algorithm and pairs with password_needs_rehash() for upgrades. Use password_hash() for new code and crypt() only for external or legacy formats.
How do I verify a password hashed with crypt?
Pass the stored hash as the salt: compare the stored value with crypt($input, $stored) using hash_equals(). The stored hash carries the algorithm, cost and salt needed to reproduce it.
Why is the salt argument required now?
PHP 8.0 made it mandatory. Previously omitting it produced a hash with a weak automatically generated salt, which was a common security bug, so the fallback was removed.
More examples
The salt prefix selects the algorithm
Each line shows the same password hashed under a different scheme, and each output carries its own prefix. That prefix is what lets a later crypt() call know how to re-hash for verification.
<?php
$pw = 'hunter2';
$salts = [
'DES ' => 'ab',
'MD5 ' => '$1$abcdefgh$',
'SHA-256 ' => '$5$abcdefghijklmnop$',
'SHA-512 ' => '$6$abcdefghijklmnop$',
'bcrypt ' => '$2y$08$abcdefghijklmnopqrstuv',
];
foreach ($salts as $name => $salt) {
echo $name, crypt($pw, $salt), PHP_EOL;
}Migrating a legacy hash to password_hash
The old hash is verified with crypt(), and because the plaintext is available at that moment it is immediately re-hashed with password_hash(). This is the standard way to retire legacy hashes without forcing a password reset.
<?php
$legacy = crypt('s3cret', '$1$oldsalt$'); // MD5-crypt from an old system
$input = 's3cret';
if (hash_equals($legacy, crypt($input, $legacy))) {
$modern = password_hash($input, PASSWORD_DEFAULT);
echo 'upgraded: ', $modern, PHP_EOL;
var_dump(password_verify($input, $modern));
} else {
echo 'login failed', PHP_EOL;
}
Discussion