/NativePasswordHasher.php Secret
Created
February 6, 2020 04:16
Star
You must be signed in to star a gist
Chain-of-responsibility pattern
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
if (!class_exists('PasswordHash')) { | |
require_once ABSPATH . WPINC . '/class-phpass.php'; | |
} | |
$password_hasher = new PasswordHasherChain([ | |
new NativePasswordHasher(), | |
new WordPressPasswordHasher(new \PasswordHash(8, true)), | |
]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Password hasher that uses the native PHP password hashing functions. | |
*/ | |
class NativePasswordHasher implements PasswordHasherInterface | |
{ | |
/** | |
* Algorithm used when hashing a password. | |
* | |
* @var int | |
*/ | |
private $algorithm; | |
/** | |
* Constructor. | |
*/ | |
public function __construct() | |
{ | |
$this->algorithm = PASSWORD_DEFAULT; | |
if (defined('PASSWORD_ARGON2ID')) { | |
$this->algorithm = PASSWORD_ARGON2ID; | |
} elseif (defined('PASSWORD_ARGON2I')) { | |
$this->algorithm = PASSWORD_ARGON2I; | |
} | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function hash_password($password) | |
{ | |
return password_hash($password, $this->algorithm); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function is_hash_supported($hash) | |
{ | |
if (0 === strpos($hash, '$argon2id$')) { | |
return defined('PASSWORD_ARGON2ID'); | |
} elseif (0 === strpos($hash, '$argon2i$')) { | |
return defined('PASSWORD_ARGON2I'); | |
} | |
return 0 === strpos($hash, '$2'); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function is_hash_valid($hash) | |
{ | |
return !password_needs_rehash($hash, $this->algorithm); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function is_password_valid($password, $hash) | |
{ | |
return password_verify($password, $hash); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Manages a chain of password hashers. | |
*/ | |
class PasswordHasherChain implements PasswordHasherInterface | |
{ | |
/** | |
* The password hashers that the chain handles. | |
* | |
* @var PasswordHasherInterface[] | |
*/ | |
private $password_hashers; | |
/** | |
* Constructor. | |
* | |
* @param PasswordHasherInterface[] $password_hashers | |
*/ | |
public function __construct(array $password_hashers = []) | |
{ | |
$this->password_hashers = array_filter($password_hashers, function ($password_hasher) { | |
return $password_hasher instanceof PasswordHasherInterface; | |
}); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function hash_password($password) | |
{ | |
$hash = array_reduce($this->password_hashers, function ($hash, PasswordHasherInterface $password_hasher) use ($password) { | |
if (empty($hash)) { | |
$hash = $password_hasher->hash_password($password); | |
} | |
return $hash; | |
}); | |
if (empty($hash)) { | |
throw new \RuntimeException('Could not create a hash for the given password.'); | |
} | |
return $hash; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function is_hash_supported($hash) | |
{ | |
return $this->get_password_hasher($hash) instanceof PasswordHasherInterface; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function is_hash_valid($hash) | |
{ | |
$password_hasher = $this->get_password_hasher($hash); | |
if (!$password_hasher instanceof PasswordHasherInterface) { | |
return false; | |
} | |
return $password_hasher->is_hash_valid($hash); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function is_password_valid($password, $hash) | |
{ | |
return array_reduce($this->password_hashers, function ($check, PasswordHasherInterface $password_hasher) use ($password, $hash) { | |
if (true !== $check) { | |
$check = $password_hasher->is_password_valid($password, $hash); | |
} | |
return $check; | |
}, false); | |
} | |
/** | |
* Get the password hasher that supports the given hash. | |
* | |
* @param string $hash | |
* | |
* @return PasswordHasherInterface|null | |
*/ | |
private function get_password_hasher($hash) | |
{ | |
return array_reduce($this->password_hashers, function ($found, PasswordHasherInterface $password_hasher) use ($hash) { | |
if (!$found instanceof PasswordHasherInterface && $password_hasher->is_hash_supported($hash)) { | |
$found = $password_hasher; | |
} | |
return $found; | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* A password hasher hashes a password using a hashing algorithm. | |
*/ | |
interface PasswordHasherInterface | |
{ | |
/** | |
* Hashes the given password. Returns null if unable to hash password. | |
* | |
* @param string $password | |
* | |
* @return string|null | |
*/ | |
public function hash_password($password); | |
/** | |
* Checks if the password hasher supports the given hash for verification. | |
* | |
* @param string $hash | |
* | |
* @return bool | |
*/ | |
public function is_hash_supported($hash); | |
/** | |
* Checks if the given hash is valid. If a hash is invalid, we need to rehash it. | |
* | |
* @param string $hash | |
* | |
* @return bool | |
*/ | |
public function is_hash_valid($hash); | |
/** | |
* Validates that the given password matches the given hash. | |
* | |
* @param string $password | |
* @param string $hash | |
* | |
* @return bool | |
*/ | |
public function is_password_valid($password, $hash); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Password hasher that uses the builtin WordPress password hasher. | |
*/ | |
class WordPressPasswordHasher implements PasswordHasherInterface | |
{ | |
/** | |
* WordPress password hasher. | |
* | |
* @var \PasswordHash | |
*/ | |
private $wordpress_hasher; | |
/** | |
* Constructor. | |
* | |
* @param \PasswordHash $wordpress_hasher | |
*/ | |
public function __construct(\PasswordHash $wordpress_hasher) | |
{ | |
$this->wordpress_hasher = $wordpress_hasher; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function hash_password($password) | |
{ | |
return $this->wordpress_hasher->HashPassword($password); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function is_hash_supported($hash) | |
{ | |
return 0 === strpos($hash, '$P$'); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function is_hash_valid($hash) | |
{ | |
return false; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function is_password_valid($password, $hash) | |
{ | |
return $this->wordpress_hasher->CheckPassword($password, $hash); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment