password hashing: the right way
DESCRIPTION
Introduces the new PHP password hashing functions and Password Validator library.TRANSCRIPT
Password Hashing: The Right Way
Jeremy Kendall - Memphis PHPJanuary 28, 2014
Wednesday, January 29, 14
Wednesday, January 29, 14
I love to code
Wednesday, January 29, 14
I love to code
I’m terribly forgetful
Wednesday, January 29, 14
I love to code
I’m terribly forgetful
I take pictures
Wednesday, January 29, 14
I love to code
I’m terribly forgetful
I take pictures
I work at OpenSky
Wednesday, January 29, 14
I’m a Little Off My Game
Wednesday, January 29, 14
What Qualifies Me To Talk About Security?
Wednesday, January 29, 14
Not Much
Wednesday, January 29, 14
Not MuchBut that will work in our favor ...
Wednesday, January 29, 14
Cryptography is Hard
Wednesday, January 29, 14
Cryptography is HardPro Tip: Leave it to the experts
Wednesday, January 29, 14
The Wrong Way
<?php
class SecurityFail{ // Encrypt Passwords for Highest Level of Security. static public function encrypt($pword) { return md5($pword); }
}
http://csiphp.com/blog/2012/02/16/encrypt-passwords-for-highest-level-of-security/
Wednesday, January 29, 14
The Right Way
http://php.net/manual/en/ref.password.php
Wednesday, January 29, 14
The Awesomer Way
Wednesday, January 29, 14
Password Hashing Functions
Wednesday, January 29, 14
Password Hashing FunctionsPro Tip: Use password_compat for PHP 5.3.7+
Wednesday, January 29, 14
Password Hashing FunctionsPro Tip: Use password_compat for PHP 5.3.7+
Pro Tip: Use phpass for PHP <= 5.3.6
Wednesday, January 29, 14
password_hash
http://www.php.net/manual/en/function.password-hash.php
Wednesday, January 29, 14
password_hash
‣ Creates a new password hash
http://www.php.net/manual/en/function.password-hash.php
Wednesday, January 29, 14
password_hash
‣ Creates a new password hash
‣ Strong, one-way hashing algorithm
http://www.php.net/manual/en/function.password-hash.php
Wednesday, January 29, 14
password_hash
‣ Creates a new password hash
‣ Strong, one-way hashing algorithm
‣ PASSWORD_DEFAULT or PASSWORD_BCRYPT
http://www.php.net/manual/en/function.password-hash.php
Wednesday, January 29, 14
password_hash
‣ Creates a new password hash
‣ Strong, one-way hashing algorithm
‣ PASSWORD_DEFAULT or PASSWORD_BCRYPT
‣ Optional cost and salt
http://www.php.net/manual/en/function.password-hash.php
Wednesday, January 29, 14
password_hash
http://www.php.net/manual/en/function.password-hash.php
Wednesday, January 29, 14
password_hash
‣ Always use PASSWORD_DEFAULT
http://www.php.net/manual/en/function.password-hash.php
Wednesday, January 29, 14
password_hash
‣ Always use PASSWORD_DEFAULT
‣ Your DB’s password field should be varchar(255)
http://www.php.net/manual/en/function.password-hash.php
Wednesday, January 29, 14
password_hash
‣ Always use PASSWORD_DEFAULT
‣ Your DB’s password field should be varchar(255)
‣ Do not use your own salt
http://www.php.net/manual/en/function.password-hash.php
Wednesday, January 29, 14
password_hash
‣ Always use PASSWORD_DEFAULT
‣ Your DB’s password field should be varchar(255)
‣ Do not use your own salt
‣ Check for an appropriate cost using the example script in the manual
http://www.php.net/manual/en/function.password-hash.php
Wednesday, January 29, 14
password_hash
$hash = password_hash('secret pass', PASSWORD_DEFAULT);
// or
$options = array('cost' => 12);$hash = password_hash('secret pass', PASSWORD_DEFAULT, $options);
Wednesday, January 29, 14
password_verify
Wednesday, January 29, 14
password_verify
‣ Verifies that a password matches a hash
Wednesday, January 29, 14
password_verify
‣ Verifies that a password matches a hash
‣ Uh, yeah, that’s about it
Wednesday, January 29, 14
password_verify
$valid = password_verify($_POST['pass'], $hashFromDb);
Wednesday, January 29, 14
password_needs_rehash
Wednesday, January 29, 14
password_needs_rehash
‣ Checks password to see if it needs to be updated
Wednesday, January 29, 14
password_needs_rehash
‣ Checks password to see if it needs to be updated
‣ Uses both hash and cost to check current hash
Wednesday, January 29, 14
password_needs_rehash
$needsRehash = password_needs_rehash($hashFromDb, PASSWORD_DEFAULT);
// or
$options = array('cost' => 12);$needsRehash = password_needs_rehash($hashFromDb, PASSWORD_DEFAULT, $options);
Wednesday, January 29, 14
That’s Awesome and Secure
Wednesday, January 29, 14
But Could It Be Awesomer, Securer, and Easier?
Wednesday, January 29, 14
Password Validator
Wednesday, January 29, 14
Password Validator
‣ Validates passwords against password_hash
Wednesday, January 29, 14
Password Validator
‣ Validates passwords against password_hash
‣Will rehash when needed
Wednesday, January 29, 14
Password Validator
‣ Validates passwords against password_hash
‣Will rehash when needed
‣Will upgrade legacy passwords
Wednesday, January 29, 14
Password Validator
‣ Validates passwords against password_hash
‣Will rehash when needed
‣Will upgrade legacy passwords
‣ Requires PHP 5.3.7+
Wednesday, January 29, 14
Password Validator
‣ Validates passwords against password_hash
‣Will rehash when needed
‣Will upgrade legacy passwords
‣ Requires PHP 5.3.7+
‣ (No version for <=5.3.6 is in the works)
Wednesday, January 29, 14
Password Validator
use JeremyKendall\Password\PasswordValidator;use JeremyKendall\Password\Result as ValidationResult;
$passwordHash = password_hash('password', PASSWORD_DEFAULT);
$validator = new PasswordValidator();$result = $validator->isValid('password', $passwordHash);
$valid = $result->isValid();$code = $result->getCode();
// $valid = true// $code = ValidationResult::SUCCESS
Wednesday, January 29, 14
Password Validatoruse JeremyKendall\Password\PasswordValidator;use JeremyKendall\Password\Result as ValidationResult;
$options = array('cost' => 9);$passwordHash = password_hash('password', PASSWORD_DEFAULT, $options);
$validator = new PasswordValidator();$validator->setOptions($options);$result = $validator->isValid('password', $passwordHash);
$valid = $result->isValid();$code = $result->getCode();
// $valid = true// $code = ValidationResult::SUCCESS
Wednesday, January 29, 14
Password Validatoruse JeremyKendall\Password\PasswordValidator;use JeremyKendall\Password\Result as ValidationResult;
$options = array('cost' => 9);$passwordHash = password_hash('password', PASSWORD_DEFAULT, $options);
$validator = new PasswordValidator();
// Remember, default cost is 10, so a cost 9 hash gets rehashed$result = $validator->isValid('password', $passwordHash);
$valid = $result->isValid();$code = $result->getCode();$hash = $result->getPassword();
// $valid = true// $code = ValidationResult::SUCCESS_PASSWORD_REHASHED// $hash = the new, rehashed password. Save it!
Wednesday, January 29, 14
Fine, But We’re Not Using password_hash Yet ...
Wednesday, January 29, 14
Decorator Pattern
http://en.wikipedia.org/wiki/Decorator_pattern
Wednesday, January 29, 14
Decorator Pattern
‣Wrap an object
http://en.wikipedia.org/wiki/Decorator_pattern
Wednesday, January 29, 14
Decorator Pattern
‣Wrap an object
‣ Change its behavior
http://en.wikipedia.org/wiki/Decorator_pattern
Wednesday, January 29, 14
Decorator Pattern
‣Wrap an object
‣ Change its behavior
‣ Dynamically attach additional responsibilities
http://en.wikipedia.org/wiki/Decorator_pattern
Wednesday, January 29, 14
PasswordValidatorInterface
interface PasswordValidatorInterface{ public function isValid($password, $passwordHash, $identity = null);
public function rehash($password);
public function setOptions(array $options);
public function getOptions();}
Wednesday, January 29, 14
Upgrade Decorator
Wednesday, January 29, 14
Upgrade Decorator
‣ Used when you’re not already using password_hash ...
Wednesday, January 29, 14
Upgrade Decorator
‣ Used when you’re not already using password_hash ...
‣ ... but you’re ready to do things the right way
Wednesday, January 29, 14
Upgrade Decorator
‣ Used when you’re not already using password_hash ...
‣ ... but you’re ready to do things the right way
‣ Accepts an instance of PasswordValidatorInterface ...
Wednesday, January 29, 14
Upgrade Decorator
‣ Used when you’re not already using password_hash ...
‣ ... but you’re ready to do things the right way
‣ Accepts an instance of PasswordValidatorInterface ...
‣ ... and a validation callback
Wednesday, January 29, 14
Upgrade Decorator
// Somewhere in your authentication scriptif (hash('sha512', $password) === $passwordHash) { $valid = true;}
$valid = false;
Wednesday, January 29, 14
Upgrade Decorator
// Same authentication check expressed as a callback$validationCallback = function ($password, $passwordHash) { if (hash('sha512', $password) === $passwordHash) { return true; }
return false;};
Wednesday, January 29, 14
Upgrade Decorator
$validator = new UpgradeDecorator( new PasswordValidator(), $validationCallback);
$passwordHash = hash('sha512', 'password');$result = $validator->isValid('password', $passwordHash);
$valid = $result->isValid();$code = $result->getCode();$hash = $result->getPassword();
// $valid = true// $code = ValidationResult::SUCCESS_PASSWORD_REHASHED// $hash = the new, rehashed password. Save it!
Wednesday, January 29, 14
Fine, But Now I Have to Test for SUCCESS_PASSWORD_REHASHED
Every Time
Wednesday, January 29, 14
Storage Decorator
Wednesday, January 29, 14
Storage Decorator
‣ Automatically stores all rehashed passwords
Wednesday, January 29, 14
Storage Decorator
‣ Automatically stores all rehashed passwords
‣ Accepts two constructor args:
Wednesday, January 29, 14
Storage Decorator
‣ Automatically stores all rehashed passwords
‣ Accepts two constructor args:
‣ Instance of PasswordValidatorInterface
Wednesday, January 29, 14
Storage Decorator
‣ Automatically stores all rehashed passwords
‣ Accepts two constructor args:
‣ Instance of PasswordValidatorInterface
‣ Instance of StorageInterface
Wednesday, January 29, 14
StorageInterface
interface StorageInterface{ /** * Updates user's password in persistent storage * * @param string $identity Unique user identifier * @param string $password New password hash */ public function updatePassword($identity, $password);}
Wednesday, January 29, 14
UserDaouse JeremyKendall\Password\Storage\StorageInterface;
class UserDao implements StorageInterface{ protected $db;
public function __construct(\PDO $db) { $this->db = $db; }
public function updatePassword($identity, $newPasswordHash) { $sql = 'UPDATE users SET passwordHash = :passwordHash WHERE identity = :identity'; $stmt = $this->db->prepare($sql); $stmt->execute(array('passwordHash' => $newPasswordHash, 'identity' => $identity));
return $this->find($identity); }}
Wednesday, January 29, 14
Storage Decorator
use JeremyKendall\Password\Decorator\StorageDecorator;
$validator = new StorageDecorator($upgradeDecorator, $userDao);
// Uses the optional third argument for PasswordValidatorInterface::isValid()$result = $validator->isValid('password', $passwordHash, '[email protected]');
// Result is the same as any other validation attempt except ...// ... ValidationResult::SUCCESS_PASSWORD_REHASHED hashes are automatically persisted!
Wednesday, January 29, 14
Recap
Wednesday, January 29, 14
Recap
‣ Use the new PHP password hashing functions
Wednesday, January 29, 14
Recap
‣ Use the new PHP password hashing functions
‣ Use Password Validator to make it dead simple
Wednesday, January 29, 14
Recap
‣ Use the new PHP password hashing functions
‣ Use Password Validator to make it dead simple
‣ If you’re not at PHP 5.5, use password_compat
Wednesday, January 29, 14
Recap
‣ Use the new PHP password hashing functions
‣ Use Password Validator to make it dead simple
‣ If you’re not at PHP 5.5, use password_compat
‣ If you’re not at PHP 5.3.7+, UPGRADE
Wednesday, January 29, 14
Recap
‣ Use the new PHP password hashing functions
‣ Use Password Validator to make it dead simple
‣ If you’re not at PHP 5.5, use password_compat
‣ If you’re not at PHP 5.3.7+, UPGRADE
‣ If you can’t upgrade, use OpenWall’s phpass
Wednesday, January 29, 14
Recap
‣ Use the new PHP password hashing functions
‣ Use Password Validator to make it dead simple
‣ If you’re not at PHP 5.5, use password_compat
‣ If you’re not at PHP 5.3.7+, UPGRADE
‣ If you can’t upgrade, use OpenWall’s phpass
‣ DO NOT ROLL YOUR OWN
Wednesday, January 29, 14
Resources
Wednesday, January 29, 14
Resources
‣ PHP Password Hashing Functions: http://php.net/password
Wednesday, January 29, 14
Resources
‣ PHP Password Hashing Functions: http://php.net/password
‣ Original password_hash RFC: https://wiki.php.net/rfc/password_hash
Wednesday, January 29, 14
Resources
‣ PHP Password Hashing Functions: http://php.net/password
‣ Original password_hash RFC: https://wiki.php.net/rfc/password_hash
‣ Password Validator: https://github.com/jeremykendall/password-validator
Wednesday, January 29, 14
Resources
‣ PHP Password Hashing Functions: http://php.net/password
‣ Original password_hash RFC: https://wiki.php.net/rfc/password_hash
‣ Password Validator: https://github.com/jeremykendall/password-validator
‣ password_compat: https://github.com/ircmaxell/password_compat
Wednesday, January 29, 14
Resources
‣ PHP Password Hashing Functions: http://php.net/password
‣ Original password_hash RFC: https://wiki.php.net/rfc/password_hash
‣ Password Validator: https://github.com/jeremykendall/password-validator
‣ password_compat: https://github.com/ircmaxell/password_compat
‣ OpenWall phpass: http://www.openwall.com/phpass/
Wednesday, January 29, 14
Thanks!
http://about.me/jeremykendall
@jeremykendall
http://365.jeremykendall.net
Wednesday, January 29, 14