crypt

Hashes a string one-way using a salt that selects the algorithm, such as bcrypt or SHA-512.

Signature
crypt(string $string, string $salt): string
  • Added in PHP 4.0

Parameters

ParameterTypeDescription
$stringstringThe string to be hashed. Marked #[\SensitiveParameter]. CRYPT_BLOWFISH truncates it at 72 bytes.
$saltstringA 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

Try it yourself
Loading editor…
Press Run to execute the code.

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.

Example · php
<?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.

Example · php
<?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

  • Be the first to comment on this lesson.