Example code for How to Safely Store a Password in 2016.
Created
February 17, 2016 04:44
-
-
Save paragonie-scott/66469d391deef2e3b875 to your computer and use it in GitHub Desktop.
Bcrypt SHA-384
This file contains hidden or 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 | |
class Foo | |
{ | |
/** | |
* This is example code. Please feel free to use it for reference but don't just copy/paste it. | |
* | |
* @param string $username Unsafe user-supplied data: The username | |
* @param string $password Unsafe user-supplied data: The password | |
* @return int The primary key for that user account | |
* @throws InvalidUserCredentialsException | |
*/ | |
public function authenticate(string $username, string $password): int | |
{ | |
// Database lookup | |
$stmt = $this->db->prepare("SELECT userid, passwordhash, legacy_password FROM user_accounts WHERE username = ?"); | |
$stmt->execute([$username]); | |
$stored = $stmt->fetch(PDO::FETCH_ASSOC); | |
if (!$stored) { | |
// No such user, throw an exception | |
throw new InvalidUserCredentialsException(); | |
} | |
if ($stored['legacy_password']) { | |
// This is the legacy password upgrade code | |
if ($this->verify(legacy_hashing_algorithm($password), $stored['passwordhash'])) { | |
$newhash = $this->hash($password); | |
$stmt = $this->db->prepare("UPDATE user_accounts SET passwordhash = ?, legacy_password = FALSE WHERE userid = ?"); | |
$stmt->execute([$newhash, $stored['userid']]); | |
// Return the user ID (integer) | |
return $stored['userid']; | |
} | |
} elseif ($this->verify($password, $stored['passwordhash'])) { | |
// This is the general purpose upgrade code e.g. if a future version of PHP upgrades to Argon2 | |
if (password_needs_rehash($stored['passwordhash'], PASSWORD_DEFAULT)) { | |
$newhash = $this->hash($password); | |
$stmt = $this->db->prepare("UPDATE user_accounts SET passwordhash = ? WHERE userid = ?"); | |
$stmt->execute([$newhash, $stored['userid']]); | |
} | |
// Return the user ID (integer) | |
return $stored['userid']; | |
} | |
// When all else fails, throw an exception | |
throw new InvalidUserCredentialsException(); | |
} | |
/** | |
* Bcrypt-SHA-384 Verification | |
* | |
* @param string $plaintext | |
* @param string $Hash | |
* @return bool | |
*/ | |
protected function verify(string $plaintext, string $hash): bool | |
{ | |
$prehash = \base64_encode( | |
\hash('sha384', $plaintext, true) | |
); | |
return \password_verify($prehash, $hash); | |
} | |
/** | |
* Creates a Bcrypt-SHA-384 hash | |
* | |
* @param string $plaintext | |
* @return string | |
*/ | |
protected function hash(string $plaintext): string | |
{ | |
return \password_hash( | |
\base64_encode( | |
\hash('sha384', $plaintext, true) | |
), | |
PASSWORD_DEFAULT, | |
['cost' => 12] | |
); | |
} | |
} |
This file contains hidden or 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 | |
try { | |
$userid = $this->authenticate($username, $password); | |
// Update the session state | |
// Redirect to the post-authentication landing page | |
} catch (InvalidUserCredentialsException $e) { | |
// Log the failure | |
// Redirect to the login form | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment