Skip to content

Instantly share code, notes, and snippets.

@rk
Last active January 4, 2018 13:15
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save rk/1070401 to your computer and use it in GitHub Desktop.
Save rk/1070401 to your computer and use it in GitHub Desktop.
Simple bcrypt object to wrap crypt() with.
<?php
// Originally by Andrew Moore
// Src: http://stackoverflow.com/questions/4795385/how-do-you-use-bcrypt-for-hashing-passwords-in-php/6337021#6337021
//
// Heavily modified by Robert Kosek, from data at php.net/crypt
class Bcrypt {
private $rounds;
private $prefix;
private $salt_prefix;
public function __construct($prefix = '', $rounds = 12) {
if(CRYPT_BLOWFISH != 1) {
throw new Exception("bcrypt not supported in this installation. See http://php.net/crypt");
}
$this->rounds = $rounds;
$this->prefix = $prefix;
// Determine if this version of PHP has the Bcrypt fix
$this->salt_prefix = version_compare(PHP_VERSION, '5.3.7') >= 0 ? '$2y' : '$2a';
}
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() {
// the base64 function uses +'s and ending ='s; translate the first, and cut out the latter
return sprintf('%s$%02d$%s', $this->salt_prefix, $this->rounds, substr(strtr(base64_encode($this->getBytes()), '+', '.'), 0, 22));
}
private function getBytes() {
$bytes = '';
if(function_exists('openssl_random_pseudo_bytes') &&
(strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) { // OpenSSL slow on Win
$bytes = openssl_random_pseudo_bytes(18);
}
if($bytes === '' && is_readable('/dev/urandom') &&
($hRand = @fopen('/dev/urandom', 'rb')) !== FALSE) {
$bytes = fread($hRand, 18);
fclose($hRand);
}
if($bytes === '') {
$key = uniqid($this->prefix, true);
// 12 rounds of HMAC must be reproduced / created verbatim, no known shortcuts.
// Changed the hash algorithm from salsa20, which has been removed from PHP 5.4.
for($i = 0; $i < 12; $i++) {
$bytes = hash_hmac('snefru256', microtime() . $bytes, $key, true);
usleep(10);
}
}
return $bytes;
}
}
?>
@multiwebinc
Copy link

I'm a bit confused about what you're saying here, especially since there are multiple hashes being calculated and you don't define which one you're talking about. My point is that the result of hash_hmac() is not used to hash anything sensitive at all. The result of that function is It is only being used as a salt and is stored in plain text. Even if a malicious hacker were to somehow determine the original uniqid() or key or microtime() used with hash_hmac(), this gives them absolutely no further information in order to determine the result of the password hashing with crypt(), which is why I cannot see any benefit of using some complicated "random" hash over simply using uniqid() as a salt for crypt().

@rk
Copy link
Author

rk commented Aug 18, 2011

My previous response was discussing the derivation of the final hash.

What you're saying is correct though, only 1 iteration is strictly required because they will know the first 11 bytes of the result anyway. And that's all that matters for bcrypt.

@rk
Copy link
Author

rk commented Jun 20, 2012

I've updated this gist by switching from salsa20 to snefru256 as the fallback method for generating random bytes.

@rk
Copy link
Author

rk commented May 30, 2013

I've updated the gist to check for the PHP 5.3.7 or higher, and use the fixed bcrypt implementation.

@Topener
Copy link

Topener commented Aug 6, 2014

You should probably remove the last line of this script. It should not include the closing tag of php

@gplcart
Copy link

gplcart commented Jan 4, 2018

unpack() returns an array, the part mt_srand(unpack('N1', uniqid())); is wrong

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment