Every now and then I hear the advice "Use bcrypt for storing passwords in PHP, bcrypt rules".
But what is bcrypt
? PHP doesn't offer any such functions, Wikipedia babbles about a file-encryption utility and Web searches just reveal a few implementations of Blowfish in different languages. Now Blowfish is also available in PHP via mcrypt
, but how does that help with storing passwords? Blowfish is a general purpose cipher, it works two ways. If it could be encrypted, it can be decrypted. Passwords need a one-way hashing function.
What is the explanation?
The bcrypt is a password hashing technique used to build password security. It is used to protect the password from hacking attacks because of the password is stored in bcrypted format. The password_hash() function in PHP is an inbuilt function which is used to create a new password hash.
In PHP, there are various cryptographic algorithms that are commonly used like md5, crypt, sha1, and bcrypt. And the most commonly used nowadays is bcrypt hashing method.
php require 'password. php'; $hash1 = password_hash('testpassword',PASSWORD_BCRYPT,array('cost' => 11)); $hash2 = password_hash('testpassword',PASSWORD_BCRYPT,array('cost' => 11)); if(password_verify($hash1,$hash2)) echo 'Pass'; else echo 'Fail'; ?>
So, you want to use bcrypt? Awesome! However, like other areas of cryptography, you shouldn't be doing it yourself. If you need to worry about anything like managing keys, or storing salts or generating random numbers, you're doing it wrong.
The reason is simple: it's so trivially easy to screw up bcrypt. In fact, if you look at almost every piece of code on this page, you'll notice that it's violating at least one of these common problems.
Leave it for the experts. Leave it for people whose job it is to maintain these libraries. If you need to make a decision, you're doing it wrong.
Instead, just use a library. Several exist depending on your requirements.
Here is a breakdown of some of the more common APIs.
Starting in PHP 5.5, a new API for hashing passwords is being introduced. There is also a shim compatibility library maintained (by me) for 5.3.7+. This has the benefit of being a peer-reviewed and simple to use implementation.
function register($username, $password) { $hash = password_hash($password, PASSWORD_BCRYPT); save($username, $hash); } function login($username, $password) { $hash = loadHashByUsername($username); if (password_verify($password, $hash)) { //login } else { // failure } }
Really, it's aimed to be extremely simple.
Resources:
This is another API that's similar to the PHP 5.5 one, and does a similar purpose.
function register($username, $password) { $bcrypt = new Zend\Crypt\Password\Bcrypt(); $hash = $bcrypt->create($password); save($user, $hash); } function login($username, $password) { $hash = loadHashByUsername($username); $bcrypt = new Zend\Crypt\Password\Bcrypt(); if ($bcrypt->verify($password, $hash)) { //login } else { // failure } }
Resources:
This is a slightly different approach to password hashing. Rather than simply supporting bcrypt, PasswordLib supports a large number of hashing algorithms. It's mainly useful in contexts where you need to support compatibility with legacy and disparate systems that may be outside of your control. It supports a large number of hashing algorithms. And is supported 5.3.2+
function register($username, $password) { $lib = new PasswordLib\PasswordLib(); $hash = $lib->createPasswordHash($password, '$2y$', array('cost' => 12)); save($user, $hash); } function login($username, $password) { $hash = loadHashByUsername($username); $lib = new PasswordLib\PasswordLib(); if ($lib->verifyPasswordHash($password, $hash)) { //login } else { // failure } }
References:
This is a layer that does support bcrypt, but also supports a fairly strong algorithm that's useful if you do not have access to PHP >= 5.3.2... It actually supports PHP 3.0+ (although not with bcrypt).
function register($username, $password) { $phpass = new PasswordHash(12, false); $hash = $phpass->HashPassword($password); save($user, $hash); } function login($username, $password) { $hash = loadHashByUsername($username); $phpass = new PasswordHash(12, false); if ($phpass->CheckPassword($password, $hash)) { //login } else { // failure } }
Resources
Note: Don't use the PHPASS alternatives that are not hosted on openwall, they are different projects!!!
If you notice, every one of these libraries returns a single string. That's because of how BCrypt works internally. And there are a TON of answers about that. Here are a selection that I've written, that I won't copy/paste here, but link to:
md5
passwords to bcryptThere are many different choices. Which you choose is up to you. However, I would HIGHLY recommend that you use one of the above libraries for handling this for you.
Again, if you're using crypt()
directly, you're probably doing something wrong. If your code is using hash()
(or md5()
or sha1()
) directly, you're almost definitely doing something wrong.
Just use a library...
bcrypt
is a hashing algorithm which is scalable with hardware (via a configurable number of rounds). Its slowness and multiple rounds ensures that an attacker must deploy massive funds and hardware to be able to crack your passwords. Add to that per-password salts (bcrypt
REQUIRES salts) and you can be sure that an attack is virtually unfeasible without either ludicrous amount of funds or hardware.
bcrypt
uses the Eksblowfish algorithm to hash passwords. While the encryption phase of Eksblowfish and Blowfish are exactly the same, the key schedule phase of Eksblowfish ensures that any subsequent state depends on both salt and key (user password), and no state can be precomputed without the knowledge of both. Because of this key difference, bcrypt
is a one-way hashing algorithm. You cannot retrieve the plain text password without already knowing the salt, rounds and key (password). [Source]
Password hashing functions have now been built directly into PHP >= 5.5. You may now use password_hash()
to create a bcrypt
hash of any password:
<?php // Usage 1: echo password_hash('rasmuslerdorf', PASSWORD_DEFAULT)."\n"; // $2y$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // For example: // $2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a // Usage 2: $options = [ 'cost' => 11 ]; echo password_hash('rasmuslerdorf', PASSWORD_BCRYPT, $options)."\n"; // $2y$11$6DP.V0nO7YI3iSki4qog6OQI5eiO6Jnjsqg7vdnb.JgGIsxniOn4C
To verify a user provided password against an existing hash, you may use the password_verify()
as such:
<?php // See the password_hash() example to see where this came from. $hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq'; if (password_verify('rasmuslerdorf', $hash)) { echo 'Password is valid!'; } else { echo 'Invalid password.'; }
There is a compatibility library on GitHub created based on the source code of the above functions originally written in C, which provides the same functionality. Once the compatibility library is installed, usage is the same as above (minus the shorthand array notation if you are still on the 5.3.x branch).
You can use crypt()
function to generate bcrypt hashes of input strings. This class can automatically generate salts and verify existing hashes against an input. If you are using a version of PHP higher or equal to 5.3.7, it is highly recommended you use the built-in function or the compat library. This alternative is provided only for historical purposes.
class Bcrypt{ private $rounds; public function __construct($rounds = 12) { if (CRYPT_BLOWFISH != 1) { throw new Exception("bcrypt not supported in this installation. See http://php.net/crypt"); } $this->rounds = $rounds; } public function hash($input){ $hash = crypt($input, $this->getSalt()); if (strlen($hash) > 13) return $hash; return false; } public function verify($input, $existingHash){ $hash = crypt($input, $existingHash); return $hash === $existingHash; } private function getSalt(){ $salt = sprintf('$2a$%02d$', $this->rounds); $bytes = $this->getRandomBytes(16); $salt .= $this->encodeBytes($bytes); return $salt; } private $randomState; private function getRandomBytes($count){ $bytes = ''; if (function_exists('openssl_random_pseudo_bytes') && (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) { // OpenSSL is slow on Windows $bytes = openssl_random_pseudo_bytes($count); } if ($bytes === '' && is_readable('/dev/urandom') && ($hRand = @fopen('/dev/urandom', 'rb')) !== FALSE) { $bytes = fread($hRand, $count); fclose($hRand); } if (strlen($bytes) < $count) { $bytes = ''; if ($this->randomState === null) { $this->randomState = microtime(); if (function_exists('getmypid')) { $this->randomState .= getmypid(); } } for ($i = 0; $i < $count; $i += 16) { $this->randomState = md5(microtime() . $this->randomState); if (PHP_VERSION >= '5') { $bytes .= md5($this->randomState, true); } else { $bytes .= pack('H*', md5($this->randomState)); } } $bytes = substr($bytes, 0, $count); } return $bytes; } private function encodeBytes($input){ // The following is code from the PHP Password Hashing Framework $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; $output = ''; $i = 0; do { $c1 = ord($input[$i++]); $output .= $itoa64[$c1 >> 2]; $c1 = ($c1 & 0x03) << 4; if ($i >= 16) { $output .= $itoa64[$c1]; break; } $c2 = ord($input[$i++]); $c1 |= $c2 >> 4; $output .= $itoa64[$c1]; $c1 = ($c2 & 0x0f) << 2; $c2 = ord($input[$i++]); $c1 |= $c2 >> 6; $output .= $itoa64[$c1]; $output .= $itoa64[$c2 & 0x3f]; } while (true); return $output; } }
You can use this code like this:
$bcrypt = new Bcrypt(15); $hash = $bcrypt->hash('password'); $isGood = $bcrypt->verify('password', $hash);
Alternatively, you may also use the Portable PHP Hashing Framework.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With