In modern web development, securely storing user passwords is a critical task. Rainbow table attacks are a common password cracking technique that relies on precomputed hash tables to quickly match original passwords. To effectively defend against such attacks, we must add "salt" to password hashes. PHP’s crypt() function is a powerful tool to combat this threat—if used correctly.
PHP’s crypt() function performs one-way encryption on strings, commonly used for hashing passwords. It supports various encryption algorithms, including traditional DES, MD5, and more secure ones like Blowfish ($2y$) and SHA-256/512 ($5$/$6$).
The function prototype is as follows:
string crypt(string $string, string $salt);
Here, $string is the original string to be encrypted, and $salt determines the encryption algorithm and its configuration.
Rainbow table attacks depend on precomputed hash values. However, if each hash uses a unique salt, attackers can’t use a universal rainbow table for matching. crypt() allows custom salt values, and when combined with strong encryption algorithms, this effectively blocks such attacks.
For example, we can use the Blowfish algorithm with a dynamic salt like this:
$password = 'user-password';
$salt = '$2y$10$' . substr(strtr(base64_encode(random_bytes(16)), '+', '.'), 0, 22);
$hash = crypt($password, $salt);
In the above code:
$2y$10$ specifies the Blowfish algorithm with a cost factor of 10;
random_bytes(16) generates a high-entropy random salt;
substr(..., 0, 22) trims it to the length required by Blowfish.
This generates a salted hash stored in $hash.
When a user logs in, you don’t need to regenerate the salt. Instead, use the stored hash from the database as the salt parameter in crypt():
$inputPassword = 'user-input';
$storedHash = '$2y$10$qzWRzYy5q3DvYbEC0KMX4O0PGWlOQxkz7v0QF5OwMqpyG0TzjSAGK'; // from database
<p>if (hash_equals($storedHash, crypt($inputPassword, $storedHash))) {<br>
echo 'Password correct';<br>
} else {<br>
echo 'Incorrect password';<br>
}<br>
This way, crypt() uses the salt and algorithm embedded in the stored hash to re-encrypt the input password and compare the results. This is a simple and secure method.
$hash = crypt($password, 'fixedsalt');
This approach is highly vulnerable to rainbow table attacks since the same password will always result in the same hash.
$hash = crypt($password, 'ab'); // DES algorithm, salt is too short
DES not only uses short salts but is also computationally cheap, making it easy to brute-force with modern hardware.
Although crypt() is secure, PHP has recommended using password_hash() and password_verify() since version 5.5. However, if you’re maintaining a legacy project or need compatibility, and still use crypt(), make sure to:
Use strong algorithms like $2y$, $5$, or $6$;
Always use random and sufficiently long salts;
Never reuse salt values;
Embed the salt within the hash itself rather than storing it separately.
// During registration
$password = $_POST['password'];
$salt = '$2y$12$' . substr(strtr(base64_encode(random_bytes(16)), '+', '.'), 0, 22);
$hash = crypt($password, $salt);
// Store $hash in the database
<p>// During login<br>
$inputPassword = $_POST['password'];<br>
$storedHash = getHashFromDatabase(); // Fetch from database</p>
<p>if (hash_equals($storedHash, crypt($inputPassword, $storedHash))) {<br>
echo 'Login successful';<br>
} else {<br>
echo 'Incorrect password';<br>
}<br>
You can visit https://m66.net/php-crypt-guide to access the online version of this guide and more examples.
Using the crypt() function correctly can effectively prevent rainbow table attacks. The key lies in employing strong encryption algorithms, random salt values, and secure comparison practices. While new projects are advised to use the password_hash() suite, understanding how to securely use crypt() is still an essential skill for every PHP developer.