Skip to content

Instantly share code, notes, and snippets.

@fernandocanizo
Created February 17, 2017 11:50
Show Gist options
  • Save fernandocanizo/3e0c2be33592c94a9b237cb4d60e6c7d to your computer and use it in GitHub Desktop.
Save fernandocanizo/3e0c2be33592c94a9b237cb4d60e6c7d to your computer and use it in GitHub Desktop.
Façade class to handle password hashing on different versions of PHP
<?php
// Creation Date: 2015.03.14
// Author: Fernando L. Canizo - http://flc.muriandre.com/
abstract class Password {
// Façade class to handle password hashing on different versions of PHP
// Uses password_hash() on PHP >= 5.5.0
// Uses crypt() on PHP < 5.5.0 and >= 5.0.0
public $phpVersion = null;
protected $function = 'password_hash';
public static function init() {
self::$phpversion = phpversion();
if(self::phpVersion < '5.0.0'):
// don't support PHP 4
throw new Exception("We need PHP version >= 5.0.0 for password hashing function to work.");
elseif(self::phpVersion < '5.5.0'):
// password_hash() appeared in v5.5.0, switch to crypt()
self::$function = 'crypt';
endif;
}
public static function getHash($password) {
$initializationVector = mcrypt_create_iv(22, MCRYPT_DEV_URANDOM);
if(false === $initializationVector):
throw new Exception("Couldn't create initialization vector for password hasshing.");
endif;
if(self::$function === 'crypt'):
// see http://php.net/security/crypt_blowfish.php about '$2y11$'
if(defined("CRYPT_BLOWFISH") && CRYPT_BLOWFISH):
$salt = '$2y$' . self::getCost() . '$' . $initializationVector;
return crypt($password, $salt);
else:
$salt = '$6$' . self::getCost() . '$' . $initializationVector;
return crypt($password, $salt);
endif;
else: // using password_hash
$option = array(
'cost' => self::getCost(),
'salt' => $initializationVector
);
if(false === ($passwordHash = password_hash($password, PASSWORD_DEFAULT, $options))):
throw new Exception("Couldn't create password hash.");
endif;
return $passwordHash;
endif;
}
/*
public static function verify($password) {
// checks a password string against saved password hash
}
*/
protected static function getCost() {
// This code will benchmark your server to determine how high of a cost
// you can afford. You want to set the highest cost that you can
// without slowing down you server too much. 8-10 is a good baseline,
// and more is good if your servers are fast enough. The code below
// aims for ≤ 50 milliseconds stretching time, which is a good baseline
// for systems handling interactive logins.
$timeTarget = 0.05; // 50 milliseconds
if('crypt' === self::$function && defined("CRYPT_BLOWFISH") && CRYPT_BLOWFISH):
$cost = 10;
do {
$cost++;
$start = microtime(true);
password_hash('test', PASSWORD_BCRYPT, array('cost' => $cost));
$end = microtime(true);
} while (($end - $start) < $timeTarget);
if($cost > 31) return 31; // cost should be between 04 and 31
return $cost;
elseif('crypt' === self::$function):
$rounds = 5000;
return 'rounds=' . $rounds . '$';
else: // password_hash
endif;
}
protected static function hashWithPasswordHash($password) {
// PASSWORD_DEFAULT: use the current best algorithm available
// Note: generated hash goes about 60 chars long, but use a DB column
// with enough space for future updates use varchar(255)
$options = array(
'cost' => self::getCost()
);
$hash = password_hash($password , PASSWORD_DEFAULT, $options);
if(false === $hash):
throw new Exception(__FILE__ . ':' . __LINE__ . ": Couldn't build a password hash.");
endif;
return $hash;
}
protected static function hashWithCrypt($password) {
}
protected static function hashWithSha512($password) {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment