Validates passwords against PHP's password_hash function using PASSWORD_DEFAULT. Will rehash when needed, and will upgrade legacy passwords with the Upgrade decorator.

Overview

Password Validator Build Status Coverage Status

Password Validator validates password_hash generated passwords, rehashes passwords as necessary, and will upgrade legacy passwords.

Read the introductory blog post: PHP Password Hashing: A Dead Simple Implementation

Password Validator is available for all versions of PHP >= 5.3.7.

Motivation

Why? Because one must always encrypt passwords for highest level of security, and the new PHP password hashing functions provide that level of security.

The Password Validator library makes it (more) trivial to use the new password hash functions in your application. Just add the validator to your authentication script and you're up and running.

The really big deal here is the ease of upgrading from your current legacy hashes to the new, more secure PHP password hash hashes. Simply wrap the PasswordValidator in the UpgradeDecorator, provide a callback to validate your existing password hashing scheme, and BOOM, you're using new password hashes in a manner completely transparent to your application's users. Nifty, huh?

Usage

Password Validation

If you're already using password_hash generated passwords in your application, you need do nothing more than add the validator in your authentication script. The validator uses password_verify to test the validity of the provided password hash.

use JeremyKendall\Password\PasswordValidator;

$validator = new PasswordValidator();
$result = $validator->isValid($_POST['password'], $hashedPassword);

if ($result->isValid()) {
    // password is valid
}

If your application requires options other than the password_hash defaults, you can set the cost option with PasswordValidator::setOptions().

$options = array(
    'cost' => 11
);
$validator->setOptions($options);

IMPORTANT: PasswordValidator uses a default cost of 10. If your existing hash implementation requires a different cost, make sure to specify it using PasswordValidator::setOptions(). If you do not do so, all of your passwords will be rehashed using a cost of 10.

Rehashing

Each valid password is tested using password_needs_rehash. If a rehash is necessary, the valid password is hashed using password_hash with the provided options. The result code Result::SUCCESS_PASSWORD_REHASHED will be returned from Result::getCode() and the new password hash is available via Result::getPassword().

if ($result->getCode() === Result::SUCCESS_PASSWORD_REHASHED) {
    $rehashedPassword = $result->getPassword();
    // Persist rehashed password
}

IMPORTANT: If the password has been rehashed, it's critical that you persist the updated password hash. Otherwise, what's the point, right?

Upgrading Legacy Passwords

You can use the PasswordValidator whether or not you're currently using password_hash generated passwords. The validator will transparently upgrade your current legacy hashes to the new password_hash generated hashes as each user logs in. All you need to do is provide a validator callback for your password hash and then decorate the validator with the UpgradeDecorator.

use JeremyKendall\Password\Decorator\UpgradeDecorator;

// Example callback to validate a sha512 hashed password
$callback = function ($password, $passwordHash, $salt) {
    if (hash('sha512', $password . $salt) === $passwordHash) {
        return true;
    }

    return false;
};

$validator = new UpgradeDecorator(new PasswordValidator(), $callback);
$result = $validator->isValid('password', 'password-hash', 'legacy-salt');

The UpgradeDecorator will validate a user's current password using the provided callback. If the user's password is valid, it will be hashed with password_hash and returned in the Result object, as above.

All password validation attempts will eventually pass through the PasswordValidator. This allows a password that has already been upgraded to be properly validated, even when using the UpgradeDecorator.

Alternate Upgrade Technique

Rather than upgrading each user's password as they log in, it's possible to preemptively rehash persisted legacy hashes all at once. PasswordValidator and the UpgradeDecorator can then be used to validate passwords against the rehashed legacy hashes, at which point the user's plain text password will be hashed with password_hash, completing the upgrade process.

For more information on this technique, please see Daniel Karp's Rehashing Password Hashes blog post, and review JeremyKendall\Password\Tests\Decorator\KarptoniteRehashUpgradeDecoratorTest to see a sample implementation.

Persisting Rehashed Passwords

Whenever a validation attempt returns Result::SUCCESS_PASSWORD_REHASHED, it's important to persist the updated password hash.

if ($result->getCode() === Result::SUCCESS_PASSWORD_REHASHED) {
    $rehashedPassword = $result->getPassword();
    // Persist rehashed password
}

While you can always perform the test and then update your user database manually, if you choose to use the Storage Decorator all rehashed passwords will be automatically persisted.

The Storage Decorator takes two constructor arguments: An instance of PasswordValidatorInterface and an instance of the JeremyKendall\Password\Storage\StorageInterface.

StorageInterface

The StorageInterface includes a single method, updatePassword(). A class honoring the interface might look like this:

<?php

namespace Example;

use JeremyKendall\Password\Storage\StorageInterface;

class UserDao implements StorageInterface
{
    public function __construct(\PDO $db)
    {
        $this->db = $db;
    }

    public function updatePassword($identity, $password)
    {
        $sql = 'UPDATE users SET password = :password WHERE username = :identity';
        $stmt = $this->db->prepare($sql);
        $stmt->execute(array('password' => $password, 'identity' => $identity));
    }
}

Storage Decorator

With your UserDao in hand, you're ready to decorate a PasswordValidatorInterface.

use Example\UserDao;
use JeremyKendall\Password\Decorator\StorageDecorator;

$storage = new UserDao($db);
$validator = new StorageDecorator(new PasswordValidator(), $storage);

// If validation results in a rehash, the new password hash will be persisted
$result = $validator->isValid('password', 'passwordHash', null, 'username');

IMPORTANT: You must pass the optional fourth argument ($identity) to isValid() when calling StorageDecorator::isValid(). If you do not do so, the StorageDecorator will throw an IdentityMissingException.

Combining Storage Decorator with Upgrade Decorator

It is possible to chain decorators together thanks to the Decorator Pattern. A great way to use this is to combine the StorageDecorator and UpgradeDecorator together to first update a legacy hash and then save it. Doing so is very simple - you just need to pass an instance of the StorageDecorator as a constructor argument to UpgradeDecorator:

use Example\UserDao;
use JeremyKendall\Password\Decorator\StorageDecorator;
use JeremyKendall\Password\Decorator\UpgradeDecorator;

// Example callback to validate a sha512 hashed password
$callback = function ($password, $passwordHash, $salt) {
    if (hash('sha512', $password . $salt) === $passwordHash) {
        return true;
    }

    return false;
};

$storage = new UserDao($db);
$storageDecorator = new StorageDecorator(new PasswordValidator(), $storage);
$validator = new UpgradeDecorator($storageDecorator, $callback);

// If validation results in a rehash, the new password hash will be persisted
$result = $validator->isValid('password', 'passwordHash', null, 'username');

Validation Results

Each validation attempt returns a JeremyKendall\Password\Result object. The object provides some introspection into the status of the validation process.

  • Result::isValid() will return true if the attempt was successful
  • Result::getCode() will return one of three possible int codes:
    • Result::SUCCESS if the validation attempt was successful
    • Result::SUCCESS_PASSWORD_REHASHED if the attempt was successful and the password was rehashed
    • Result::FAILURE_PASSWORD_INVALID if the attempt was unsuccessful
  • Result::getPassword() will return the rehashed password, but only if the password was rehashed

Database Schema Changes

As mentioned above, because this library uses the PASSWORD_DEFAULT algorithm, it's important your password field be VARCHAR(255) to account for future updates to the default password hashing algorithm.

Helper Scripts

After running composer install, there are two helper scripts available, both related to the password hash functions.

version-check

If you're not already running PHP 5.5+, you should run version-check to ensure your version of PHP is capable of using password-compat, the userland implementation of the PHP password hash functions. Run ./vendor/bin/version-check from the root of your project. The result of the script is pass/fail.

cost-check

The default cost used by password_hash is 10. This may or may not be appropriate for your production hardware, and it's entirely likely you can use a higher cost than the default. cost-check is based on the finding a good cost example in the PHP documentation. Simply run ./vendor/bin/cost-check from the command line and an appropriate cost will be returned.

NOTE: The default time target is 0.2 seconds. You may choose a higher or lower target by passing a float argument to cost-check, like so:

$ ./vendor/bin/cost-check 0.4
Appropriate 'PASSWORD_DEFAULT' Cost Found:  13

Installation

The only officially supported method of installation is via Composer.

Running the following command will add the latest version of the library to your project:

$ composer require jeremykendall/password-validator

You can update to the latest version with this command:

$ composer update jeremykendall/password-validator

If you're not already using Composer in your project, add the autoloader to your project:

<?php

require_once '../vendor/autoload.php'

You're now ready to begin using the Password Validator.

Contributing

Pull requests are always welcome. Please review the CONTRIBUTING.md document before submitting pull requests.

Comments
  • PHP7 - password_hash(): Use of the 'salt' option to password_hash is deprecated

    PHP7 - password_hash(): Use of the 'salt' option to password_hash is deprecated

    Warning: password_hash(): Use of the 'salt' option to password_hash is deprecated

    This is emitted from lines 51-54 of UpgradeDecorator::isValid().

            if ($isValid === true) {
                $passwordHash = password_hash($password, PASSWORD_DEFAULT, array(
                    'cost' => 4,
                    'salt' => 'CostAndSaltForceRehash',
                ));
            }
    

    ~~I'm assuming that this is just a temporary hash that can then be re-hashed in PasswordValidator again. And I'm guessing that you have to make sure you have a reasonably unique salt/cost or password_needs_rehash will return false. Not sure what to suggest.~~

    Could the salt not be removed here? If it produces a hash which matches the options set for PasswordValidator then all will be good; otherwise it will just be re-hashed again by PasswordValidator. Right?

    opened by garethellis36 12
  • RFC: fix PHP7 warning with

    RFC: fix PHP7 warning with "force rehash" approach

    This attempts to address #16.

    As discussed in that issue and on this commit, the 'salt' option must be removed from password_hash() in UpgradeDecorator in order for PHP7 to not emit warnings.

    However, without further work re-hashing in PasswordValidator does not work without the salt. This PR uses a "slightly naughty" workaround by telling PasswordValidator to force a rehash.

    opened by garethellis36 9
  • Example with sqlite?

    Example with sqlite?

    Hi @jeremykendall,

    I'm keen on using password-validator; however, I'm unsure if it'll work with sqlite. Does it? If so, would you consider putting together or sketching out an example?

    Thank you.

    question 
    opened by paxperscientiam 5
  • Add example of using StorageDecorator with UpgradeDecorator to README

    Add example of using StorageDecorator with UpgradeDecorator to README

    @garethellis36 pointed out an example of using those decorators together is missing from the docs. Gotta add it in.

    If anyone is dropping by, you can check out these examples from from the test suite.

    documentation 
    opened by jeremykendall 4
  • Remove custom salt example

    Remove custom salt example

    The ability to provide custom salts will be removed in future versions of PHP, as discussed here:

    https://marc.info/?t=142782791400003&r=1&w=2

    Even though password_hash() and your library currently support this, I suggest removing option from the usage example, because people will use it incorrectly and create much worse salts than it does by default.

    opened by nicoSWD 4
  • Available for usage on: 5.3.3-7+squeeze19

    Available for usage on: 5.3.3-7+squeeze19

    Hi, maybe a weird question but one of our older servers currently requires we use 5.3.3-7+squeeze19, i know in the composer.json file it specs we need 5.3.7+, but what are the specific requirements of this vs 5.3.3? I saw they "Updated crypt_blowfish to 1.2", but would this lib still work before that fix? If none, could we change the requirement in this file down to 5.3.3?

    opened by stilliard 4
  • Rehash branch

    Rehash branch

    As requested, a pull request for a legacy hash rehasher. However, having finished this, I'm not sure it really adds much--the actual functional part is very simple. The original intent was to hide all knowledge of the php password_hash() and password_verify() functions from the user, but I realized that the implementation of the testing for legacy hashes still requires the user to use password_verify in the validationCallback, so that benefit isn't really achieved. I would completely understand if you decide that this isn't worth maintaining, and decline the pull request.

    wontfix 
    opened by karptonite 3
  • wp-cli/php-cli-tools could not be found in any version

    wp-cli/php-cli-tools could not be found in any version

    Composer error during fresh install:

    $ composer install          
    Loading composer repositories with package information
    Installing dependencies (including require-dev)
    Your requirements could not be resolved to an installable set of packages.
    
      Problem 1
        - The requested package wp-cli/php-cli-tools could not be found in any version, there may be a typo in the package name.
    
    Potential causes:
     - A typo in the package name
     - The package is not available in a stable-enough version according to your minimum-stability setting
       see <https://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion> for more details.
    
    Read <http://getcomposer.org/doc/articles/troubleshooting.md> for further common problems.
    
    bug 
    opened by jeremykendall 3
  • Upgrade Decorator should not have user defined salt or cost

    Upgrade Decorator should not have user defined salt or cost

    https://github.com/jeremykendall/password-validator/blob/develop/src/JeremyKendall/Password/Decorator/UpgradeDecorator.php#L53

    Has a hardcoded salt and cost.

    Please see http://php.net/manual/en/function.password-hash.php for information about why both should be removed or updated to allow the user to define them if they have need.

    question wontfix 
    opened by th3fallen 1
  • Use tilde in version spec

    Use tilde in version spec

    Was getting an error when trying to install package, with composer returning that it could not find a version of "wp-cli" that matched "v0.9.4". Upon checking packagist, found that this version was patched and now called "v0.9.4-patch46". Adding the tilde in front of the current version will allow this to download properly and if patched again, remain working seamlessly since it sets "v0.9.4" to a minimum allowable version.

    opened by unisys12 1
  • Have you implemented Password Validator anywhere?

    Have you implemented Password Validator anywhere?

    If you have implemented Password Validator, I'd be awfully grateful if you'd take the time to answer these five questions. Thanks!

    • Can you say where?
    • Was the implementation as easy and trouble free as I promised?
    • Are you using the UpgradeDecorator?
    • Are you using the StorageDecorator?
    • Any additional comments or suggestions?
    question 
    opened by jeremykendall 0
  • Make algorithm for password_hash configurable.

    Make algorithm for password_hash configurable.

    This allows to use the new Argon2i algorithm introduced in PHP 7.2 (https://wiki.php.net/rfc/argon2_password_hash).

    Since this feature only makes sense for PHP 7.2, it's also fine to bump the minimum version requirement to 7.0, remove the not needed compatibility library and upgrade PhpUnit so that the test suite does not throw any warnings or deprecation notices on 7.2.

    opened by bashofmann 0
  • Update documentation to point out deprecated salt (as of PHP 7).

    Update documentation to point out deprecated salt (as of PHP 7).

    Report via this tweet: https://twitter.com/devbrilliance/status/683447669709271040.

    (Moved from https://github.com/jeremykendall/slim-auth/issues/34 to here, the correct repo.)

    documentation 
    opened by jeremykendall 0
  • Write implementation example for upgrading user passwords to password_hash

    Write implementation example for upgrading user passwords to password_hash

    See discussion in #7.

    what would people learn that they wouldn't learn from what I already wrote?

    I'm a big fan of examples of practical application. I see a big opportunity for code examples that would flesh out the information in the blog post.

    Maybe just seeing a complete example, using Password Validator?

    That and an example of what the DB update might look like. Devs who haven't implemented safe password hashing and storage may not (would likely not?) have the skill set to plan out the db update and migration.

    If you want to make up a first draft sample, I'd be happy to work on it, though.

    Will do.

    documentation 
    opened by jeremykendall 0
Releases(3.0.4)
  • 3.0.4(Jan 26, 2016)

    Password Validator is now PHP 7.0 compatible! Thanks to @nicoSWD for documentation updates and to @garethellis36 for the PHP 7.0 compatibility PR.

    Source code(tar.gz)
    Source code(zip)
  • 3.0.3(Mar 25, 2015)

  • 3.0.2(Sep 18, 2014)

  • 3.0.1(May 14, 2014)

  • 3.0.0(May 14, 2014)

    Password Validator now supports salts for legacy hashing

    Adds an optional $legacySalt argument as the third argument to PasswordValidatorInterface::isValid. This allows the use of a salt in the $validatorCallback to validate legacy hashes in the UpgradeDecorator. Usage of salts in the UpgradeDecorator was previously not possible.

    Which means you can now ...

    ... use @karptonite's Rehashing Password Hashes password security upgrade technique, which I highly recommend. Please read and re-read his post, make sure you understand it, and then implement user authentication against the new hashes with Password Validator and the UpgradeDecorator (A sample implementation is demonstrated in the JeremyKendall\Password\Tests\Decorator\KarptoniteRehashUpgradeDecoratorTest unit test).

    IMPORTANT: This is a backwards incompatible change

    • The PasswordValidatorInterface::isValid method signature has changed
    • Only users of the StorageDecorator should be impacted.

    If you're using the StorageDecorator, your call to PasswordValidatorInterface::isValid must now include the $salt argument, even if it's set to null.

    • <= 2.0.3: $storageDecorator->isValid('password', 'hash', 'username');
    • = 3.0.0: $storageDecorator->isValid('password', 'hash', $salt = null, 'username');

    Source code(tar.gz)
    Source code(zip)
  • 2.0.3(Mar 14, 2014)

    This release is to fix my Semantic Versioning mistake of releasing 1.0.0 as the production release after releasing 2.0.3-beta as the final beta. 2.0.3 === 1.0.0. The version number is solely intended to get back on track with semver.

    Source code(tar.gz)
    Source code(zip)
  • 2.0.3-beta(Feb 15, 2014)

  • 2.0.2-beta(Jan 29, 2014)

    • Fixes rehash bug
      • password_needs_rehash didn't get options passed
      • password_needs_rehash always returned true when cost != 10
    • Various housekeeping/cruft updates
    Source code(tar.gz)
    Source code(zip)
  • 2.0.1-beta(Jan 12, 2014)

  • 2.0.0-beta(Jan 11, 2014)

    • Updates minimum PHP version from 5.3 to 5.3.7, potentially a backwards incompatible change for users of PHP >= 5.3 but <= 5.3.6.
    • Updates README.md
    • Updates composer.json with metadata additions and changes
    Source code(tar.gz)
    Source code(zip)
  • 1.1.0-beta(Jan 5, 2014)

  • 1.0.0-beta(Jan 4, 2014)

Owner
Jeremy Kendall
Jeremy Kendall
PHP Library to generate random passwords

Password Generator Library Simple library for generating random passwords. Requirements PHP >= 7.1 We only support PHP 7.3+ Installation Install Compo

Daniel Platt 256 Dec 9, 2022
A password policy enforcer for PHP and JavaScript

PasswordPolicy A tool for checking and creating password policies in PHP and JS. Installation Use composer to setup an autoloader php composer.phar in

Anthony Ferrara 74 Dec 2, 2022
A live chat which utilises Socket.io and validates messages sent against a MySQL Table.

Socket.io Shoutbox with MySQL Validation The purpouse of this demonstration is to allow users to enter messages in either a public or private chat. Th

Jefff 9 Oct 14, 2022
It validates PSR-7 messages (HTTP request/response) against OpenAPI specifications

NOTICE - THE PACKAGE HAS BEEN CONTRIBUTED TO THE PHP LEAGUE Go to https://github.com/thephpleague/openapi-psr7-validator This package is here for exis

Dmitry Lezhnev 167 Sep 29, 2022
It validates PSR-7 messages (HTTP request/response) against OpenAPI specifications

OpenAPI PSR-7 Message (HTTP Request/Response) Validator This package can validate PSR-7 messages against OpenAPI (3.0.x) specifications expressed in Y

The League of Extraordinary Packages 421 Jan 3, 2023
A simple package to validate against common passwords and help keep your application secure.

common-passwords A simple package to validate against common passwords and help keep your application secure. composer require crumbls/common-password

Crumbls 4 Oct 16, 2021
A simple, standalone, modern PHP class inspector and mapper library, wrapping PHPs native reflection in a fluent interface

A simple, standalone, modern PHP class inspector and mapper library, wrapping PHPs native reflection in a fluent interface.

smpl 9 Sep 1, 2022
A fluent extension to PHPs DateTime class.

Expressive Date A fluent extension to PHPs DateTime class. Table of Contents Installation Composer Manually Laravel 4 Usage Getting Instances Quick He

Jason Lewis 258 Oct 9, 2021
Making multiple identical function calls has the same effect as making a single function call.

Making multiple identical function calls has the same effect as making a single function call.

李铭昕 4 Oct 16, 2021
Repo do vídeo do youtube de Design Patterns - Decorator

DesignPatternsPHP-Decorator Repo do vídeo do Youtube de Design Patterns - Decorator Link do vídeo Decorator em PHP 8 Imagem de exemplo Link do cadastr

Leonardo Tumadjian 10 Aug 18, 2022
Easily decorate your method calls with laravel-decorator package

?? Laravel Decorator Decorator pattern in laravel apps Made with ❤️ for smart clean coders A try to port "decorator" feature from python language to l

Iman 126 Jan 1, 2023
Legacy repository - archives past feature requests/bug reports

Scrutinizer Legacy repository for archiving past feature requests and bug reports. For reporting new bugs/feature requests, please use the ticket syst

Continuous Inspection 141 Aug 4, 2021
PHP 8.1 like legacy enum (Experimental Alpha Version)

flux-legacy-enum PHP 8.1 like legacy enum Experimental Alpha Version Installation COPY --from=docker-registry.fluxpublisher.ch/flux-enum/legacy:latest

fluxlabs 1 Dec 12, 2022
TeamCal Pro is a legacy web application of a day-based calendar

TeamCal Pro is a legacy web application of a day-based calendar. It's generic purpose is the absence management of project teams, more precisely of their members.

George Lewe 1 Jan 7, 2022
Is an Extension of Laravel View Class which compiles String Template on the fly. It automatically detects changes on your string template and recompiles it if needed.

Laravel-fly-view Is an Extension of Laravel View Class which compiles String Template on the fly. It automatically detects changes on your string temp

John Turingan 16 Jul 17, 2022
Automatic multi-tenancy for Laravel. No code changes needed.

Tenancy for Laravel — stancl/tenancy Automatic multi-tenancy for your Laravel app. You won't have to change a thing in your application's code. ✔️ No

Samuel Štancl 2.7k Jan 3, 2023
FacEssential is a Core for PMMP, it gathers all kind of plugins needed to create a faction server. It was created from scratch by Clouds#0667.

FacEssential FacEssential is a Core for PMMP, it gathers all kind of plugins needed to create a faction server. It was created from scratch by Clouds#

Zoumi 10 Jun 13, 2022
The Templating component provides all the tools needed to build any kind of template system.

Templating Component The Templating component provides all the tools needed to build any kind of template system. It provides an infrastructure to loa

Symfony 999 Dec 25, 2022
A simple scaffold used for what's needed to spin up a Composer-based WordPress plugin.

A simple scaffold used for what's needed to spin up a Composer-based WordPress plugin.

Tom McFarlin 29 Dec 29, 2022