PHP_ password_hash - Manual
PHP_ password_hash - Manual
Documentation
Get Involved
Help
Search docs
PHP 8.4.6 Released!
Getting Started
Introduction
A simple tutorial
Language Reference
Basic syntax
Types
Variables
Constants
Expressions
Operators
Control Structures
Functions
Classes and Objects
Namespaces
Enumerations
Errors
Exceptions
Fibers
Generators
Attributes
References Explained
Predefined Variables
Predefined Exceptions
Predefined Interfaces and Classes
Predefined Attributes
Context options and parameters
Supported Protocols and Wrappers
Security
Introduction
General considerations
Installed as CGI binary
Installed as an Apache module
Session Security
Filesystem Security
Database Security
Error Reporting
User Submitted Data
Hiding PHP
Keeping Current
Features
HTTP authentication with PHP
Cookies
Sessions
Handling file uploads
Using remote files
Connection handling
Persistent Database Connections
Command line usage
Garbage Collection
DTrace Dynamic Tracing
Function Reference
Affecting PHP's Behaviour
Audio Formats Manipulation
Authentication Services
Command Line Specific Extensions
Compression and Archive Extensions
Cryptography Extensions
Database Extensions
Date and Time Related Extensions
File System Related Extensions
Human Language and Character Encoding Support
Image Processing and Generation
Mail Related Extensions
Mathematical Extensions
Non-Text MIME Output
Process Control Extensions
Other Basic Extensions
Other Services
Search Engine Extensions
Server Specific Extensions
Session Extensions
Text Processing
Variable and Type Related Extensions
Web Services
Windows Only Extensions
XML Manipulation
GUI Extensions
Keyboard Shortcuts
?
This help
j
Next menu item
k
Previous menu item
gp
Previous man page
gn
Next man page
G
Scroll to bottom
gg
Scroll to top
gh
Goto homepage
gs
Goto search
(current page)
/
Focus search box
password_needs_rehash »
« password_get_info
PHP Manual
Function Reference
Cryptography Extensions
Password Hashing
Password Hashing Functions
Change language: English
password_hash
(PHP 5 >= 5.5.0, PHP 7, PHP 8)
Description ¶
password_hash() creates a new password hash using a strong one-way hashing algorithm.
PASSWORD_DEFAULT - Use the bcrypt algorithm (default as of PHP 5.5.0). Note that this constant is designed to
change over time as new and stronger algorithms are added to PHP. For that reason, the length of the result from
using this identifier can change over time. Therefore, it is recommended to store the result in a database column
that can expand beyond 60 bytes (255 bytes would be a good choice).
PASSWORD_BCRYPT - Use the bcrypt algorithm to create the hash. This will produce a standard crypt() compatible
hash using the $2y$ identifier.
PASSWORD_ARGON2I - Use the Argon2i hashing algorithm to create the hash. This algorithm is only available if PHP
has been compiled with Argon2 support.
PASSWORD_ARGON2ID - Use the Argon2id hashing algorithm to create the hash. This algorithm is only available if
PHP has been compiled with Argon2 support.
salt (string) - to manually provide a salt to use when hashing the password. Note that this will override and
prevent a salt from being automatically generated.
If omitted, a random salt will be generated by password_hash() for each password hashed. This is the intended
mode of operation.
Warning
The salt option is deprecated. It is now preferred to simply use the salt that is generated by default. As of PHP
8.0.0, an explicitly given salt is ignored.
cost (int) - which denotes the algorithmic cost that should be used. Examples of these values can be found on the
crypt() page.
If omitted, a default value of 12 will be used. This is a good baseline cost, but it should be adjusted depending on
hardware used.
memory_cost (int) - Maximum memory (in kibibytes) that may be used to compute the Argon2 hash. Defaults to
PASSWORD_ARGON2_DEFAULT_MEMORY_COST.
time_cost (int) - Maximum amount of time it may take to compute the Argon2 hash. Defaults to
PASSWORD_ARGON2_DEFAULT_TIME_COST.
threads (int) - Number of threads to use for computing the Argon2 hash. Defaults to
PASSWORD_ARGON2_DEFAULT_THREADS.
Warning
Only available when PHP uses libargon2, not with libsodium implementation.
Parameters ¶
password
Caution
Using the PASSWORD_BCRYPT as the algorithm, will result in the password parameter being truncated to a maximum
length of 72 bytes.
algo
A password algorithm constant denoting the algorithm to use when hashing the password.
options
An associative array containing options. See the password algorithm constants for documentation on the
supported options for each algorithm.
Return Values ¶
The used algorithm, cost and salt are returned as part of the hash. Therefore, all information that's needed to verify the
hash is included in it. This allows the password_verify() function to verify the hash without needing separate storage for
the salt or algorithm information.
Changelog ¶
Version Description
8.4.0 The default value of the cost option of the PASSWORD_BCRYPT algorithm was increased from 10 to 12.
password_hash() now sets the underlying Random\RandomException as the Exception::$previous exception
8.3.0
when a ValueError is thrown due to a failure in the salt generation.
password_hash() no longer returns false on failure, instead a ValueError will be thrown if the password
8.0.0
hashing algorithm is not valid, or an Error if the password hashing failed for an unknown error.
8.0.0 The algo parameter is nullable now.
7.4.0 The algo parameter expects a string now, but still accepts ints for backward compatibility.
7.4.0 The sodium extension provides an alternative implementation for Argon2 passwords.
7.3.0 Support for Argon2id passwords using PASSWORD_ARGON2ID was added.
7.2.0 Support for Argon2i passwords using PASSWORD_ARGON2I was added.
Examples ¶
<?php
echo password_hash("rasmuslerdorf", PASSWORD_DEFAULT);
?>
$2y$12$4Umg0rCJwMswRw/l.SwHvuQV01coP0eWmGzd61QH2RvAOMANUBGC.
<?php
$options = [
// Increase the bcrypt cost from 12 to 13.
'cost' => 13,
];
echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options);
?>
$2y$13$xeDfQumlmdm0Sco.4qmH1OGfUUmOcuRmfae0dPJhjX1Bq0yYhqbNi
This code will benchmark the machine to determine how high of a cost can be used without deteriorating user
experience. It is recommended to set the highest cost that does not slow down other operations the machine needs to
perform. 11 is a good baseline, and more is better if the machine is fast enough. The code below aims for ≤ 350
milliseconds stretching time, which is an appropriate delay for systems handling interactive logins.
<?php
$timeTarget = 0.350; // 350 milliseconds
$cost = 11;
do {
$cost++;
$start = microtime(true);
password_hash("test", PASSWORD_BCRYPT, ["cost" => $cost]);
$end = microtime(true);
} while (($end - $start) < $timeTarget);
<?php
echo 'Argon2i hash: ' . password_hash('rasmuslerdorf', PASSWORD_ARGON2I);
?>
Notes ¶
Caution
It is strongly recommended not to provide an explicit salt for this function. A secure salt will automatically be created if
no salt is specified.
As noted above, providing the salt option in PHP 7.0.0 will generate a deprecation warning. Support for providing a
salt explicitly has been removed in PHP 8.0.0.
Note:
It is recommended to test this function on the machine used, adjusting the cost parameter(s) so that
execution of the function takes less than 350 milliseconds for interactive logins. The script in the above
example will help choosing an appropriate bcrypt cost for the given machine.
Note: Updates to supported algorithms by this function (or changes to the default one) must follow the
following rules:
Any new algorithm must be in core for at least 1 full release of PHP prior to becoming default. So if,
for example, a new algorithm is added in 7.5.5, it would not be eligible for default until 7.7 (since 7.6
would be the first full release). But if a different algorithm was added in 7.6.0, it would also be
eligible for default at 7.7.0.
The default should only change in a full release (7.3.0, 8.0.0, etc) and not in a revision release. The
only exception to this is in an emergency when a critical security flaw is found in the current default.
See Also ¶
password_verify() - Verifies that a password matches a hash
password_needs_rehash() - Checks if the given hash matches the given options
crypt() - One-way string hashing
sodium_crypto_pwhash_str() - Get an ASCII-encoded hash
Found A Problem?
Learn How To Improve This Page • Submit a Pull Request • Report a Bug
+add a note
Since there is no pepper parameter for password_hash (even though Argon2 has a "secret" parameter, PHP does
not allow to set it), the correct way to mix in a pepper is to use hash_hmac(). The "add note" rules of
php.net say I can't link external sites, so I can't back any of this up with a link to NIST, Wikipedia,
posts from the security stackexchange site that explain the reasoning, or anything... You'll have to verify
this manually. The code:
// config.conf
pepper=c1isvFdxMDdmjOlvxpecFw
<?php
// register.php
$pepper = getConfigVariable("pepper");
$pwd = $_POST['password'];
$pwd_peppered = hash_hmac("sha256", $pwd, $pepper);
$pwd_hashed = password_hash($pwd_peppered, PASSWORD_ARGON2ID);
add_user_to_database($username, $pwd_hashed);
?>
<?php
// login.php
$pepper = getConfigVariable("pepper");
$pwd = $_POST['password'];
$pwd_peppered = hash_hmac("sha256", $pwd, $pepper);
$pwd_hashed = get_pwd_from_db($username);
if (password_verify($pwd_peppered, $pwd_hashed)) {
echo "Password matches.";
}
else {
echo "Password incorrect.";
}
?>
Note that this code contains a timing attack that leaks whether the username exists. But my note was over
the length limit so I had to cut this paragraph out.
Also note that the pepper is useless if leaked or if it can be cracked. Consider how it might be exposed,
for example different methods of passing it to a docker container. Against cracking, use a long randomly
generated value (like in the example above), and change the pepper when you do a new install with a clean
user database. Changing the pepper for an existing database is the same as changing other hashing
parameters: you can either wrap the old value in a new one and layer the hashing (more complex), you
compute the new password hash whenever someone logs in (leaving old users at risk, so this might be okay
depending on what the reason is that you're upgrading).
Why does this work? Because an attacker does the following after stealing the database:
password_verify("a", $stolen_hash)
password_verify("b", $stolen_hash)
...
password_verify("z", $stolen_hash)
password_verify("aa", $stolen_hash)
etc.
(More realistically, they use a cracking dictionary, but in principle, the way to crack a password hash is
by guessing. That's why we use special algorithms: they are slower, so each verify() operation will be
slower, so they can try much fewer passwords per hour of cracking.)
Now what if you used that pepper? Now they need to do this:
Without that $secret (the pepper), they can't do this computation. They would have to do:
If your pepper contains 128 bits of entropy, and so long as hmac-sha256 remains secure (even MD5 is
technically secure for use in hmac: only its collision resistance is broken, but of course nobody would use
MD5 because more and more flaws are found), this would take more energy than the sun outputs. In other
words, it's currently impossible to crack a pepper that strong, even given a known password and salt.
up
down
5
bhare at duck dot com ¶
1 year ago
If you are you going to use bcrypt then you should pepper the passwords with random large string, as
commodity hardware can break bcrypt 8 character passwords within an hour;
https://www.tomshardware.com/news/eight-rtx-4090s-can-break-passwords-in-under-an-hour
up
down
41
nicoSWD ¶
11 years ago
I agree with martinstoeckli,
don't create your own salts unless you really know what you're doing.
By default, it'll use /dev/urandom to create the salt, which is based on noise from device drivers.
Both have been around for many years, and are considered secure for cryptography (the former probably more
than the latter, though).
Don't try to outsmart these defaults by creating something less secure. Anything that is based on rand(),
mt_rand(), uniqid(), or variations of these is *not* good.
up
down
28
Lyo Mi ¶
9 years ago
Please note that password_hash will ***truncate*** the password at the first NULL-byte.
http://blog.ircmaxell.com/2015/03/security-issue-combining-bcrypt-with.html
If you use anything as an input that can generate NULL bytes (sha1 with raw as true, or if NULL bytes can
naturally end up in people's passwords), you may make your application much less secure than what you might
be expecting.
The password
$a = "\01234567";
is zero bytes long (an empty password) for bcrypt.
The workaround, of course, is to make sure you don't ever pass NULL-bytes to password_hash.
up
down
1
fullstadev at gmail dot com ¶
11 months ago
Similar to another post made here about the use of strings holding null-bytes within password_hash(), I
wanted to be a little more precise, as we've had quite some issues now.
I've had a project of an application generating random hashes (CSPRN). What they've done is that they've
used random_bytes(32), and the applied password_hash() to that obtained string, with the bcrypt algorithm.
This on one side led to the fact that sometimes, random_bytes() generated a string with null-bytes,
actually resulting to an error in their call to password_hash() (PHP v 8.2.18). Thanks to that ("Bcrypt
password must not contain a null character") I modified the the function generating random hashes to
encoding the obtained binary random string with random_bytes() using bin2hex() (or base64 or whatever), to
assure that the string to be hashed has no null-bytes.
I then just wanted to add that, when you use the bcrypt algorithm, make sure to remember that bcrypt
truncates your password at 72 characters. When encoding your random string (e.g. generated using
random_bytes()), this will convert your string from binary to hex representation, e.g. doubling its length.
What you generally want is that your entire password is still contained within the 72 characters limit, to
be sure that your entire "random information" gets hashes, and not only part of it.
up
down
15
martinstoeckli ¶
12 years ago
In most cases it is best to omit the salt parameter. Without this parameter, the function will generate a
cryptographically safe salt, from the random source of the operating system.
up
down
3
ms1 at rdrecs dot com ¶
5 years ago
Timing attacks simply put, are attacks that can calculate what characters of the password are due to speed
of the execution.
More at...
https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy
I have added code to phpnetcomment201908 at lucb1e dot com's suggestion to make this possible "timing
attack" more difficult using the code phpnetcomment201908 at lucb1e dot com posted.
$pph_strt = microtime(true);
//...
/*The code he posted for login.php*/
//...
usleep ( $wait );
Note I suggest changing the wait time to suit your needs but make sure that it is more than than the
highest execution time the script takes on your server.
Also, this is my workaround to obfuscate the execution time to nullify timing attacks. You can find an in-
depth discussion and more from people far more equipped than I for cryptography at the link I posted. I do
not believe this was there but there are others. It is where I found out what timing attacks were as I am
new to this but would like solid security.
up
down
8
Mike Robinson ¶
10 years ago
For passwords, you generally want the hash calculation time to be between 250 and 500 ms (maybe more for
administrator accounts). Since calculation time is dependent on the capabilities of the server, using the
same cost parameter on two different servers may result in vastly different execution times. Here's a quick
little function that will help you determine what cost parameter you should be using for your server to
make sure you are within this range (note, I am providing a salt to eliminate any latency caused by
creating a pseudorandom salt, but this should not be done when hashing passwords):
<?php
/**
* @Param int $min_ms Minimum amount of time in milliseconds that it should take
* to calculate the hashes
*/
function getOptimalBcryptCostParameter($min_ms = 250) {
for ($i = 4; $i < 31; $i++) {
$options = [ 'cost' => $i, 'salt' => 'usesomesillystringforsalt' ];
$time_start = microtime(true);
password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options);
$time_end = microtime(true);
if (($time_end - $time_start) * 1000 > $min_ms) {
return $i;
}
}
}
echo getOptimalBcryptCostParameter(); // prints 12 in my case
?>
+add a note
Search docs