{"id":13622895,"url":"https://github.com/jeremykendall/password-validator","last_synced_at":"2025-04-06T08:13:24.724Z","repository":{"id":12895197,"uuid":"15572115","full_name":"jeremykendall/password-validator","owner":"jeremykendall","description":"Validates passwords against PHP's password_hash function using PASSWORD_DEFAULT. Will rehash when needed, and will upgrade legacy passwords with the Upgrade decorator.","archived":false,"fork":false,"pushed_at":"2018-04-07T08:42:37.000Z","size":73,"stargazers_count":143,"open_issues_count":4,"forks_count":16,"subscribers_count":9,"default_branch":"develop","last_synced_at":"2025-03-30T06:10:04.845Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jeremykendall.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-01-01T23:50:52.000Z","updated_at":"2025-02-16T04:09:58.000Z","dependencies_parsed_at":"2022-09-13T22:42:17.310Z","dependency_job_id":null,"html_url":"https://github.com/jeremykendall/password-validator","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremykendall%2Fpassword-validator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremykendall%2Fpassword-validator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremykendall%2Fpassword-validator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremykendall%2Fpassword-validator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jeremykendall","download_url":"https://codeload.github.com/jeremykendall/password-validator/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247451665,"owners_count":20940944,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T21:01:25.456Z","updated_at":"2025-04-06T08:13:24.707Z","avatar_url":"https://github.com/jeremykendall.png","language":"PHP","funding_links":[],"categories":["密码","目录","Table of Contents","PHP","密码 Passwords","Security","Passwords","密码( Passwords )"],"sub_categories":["密码 Passwords","Passwords","Globalization"],"readme":"# Password Validator [![Build Status](https://travis-ci.org/jeremykendall/password-validator.png?branch=master)](https://travis-ci.org/jeremykendall/password-validator) [![Coverage Status](https://coveralls.io/repos/jeremykendall/password-validator/badge.png?branch=master)](https://coveralls.io/r/jeremykendall/password-validator?branch=master)\n\n**Password Validator** *validates* [`password_hash`][2] generated passwords, *rehashes*\npasswords as necessary, and will *upgrade* legacy passwords.\n\nRead the introductory blog post: [PHP Password Hashing: A Dead Simple Implementation][9]\n\n*Password Validator is available for all versions of PHP \u003e= 5.3.7.*\n\n## Motivation\n\nWhy? Because one must always[ encrypt passwords for highest level of\nsecurity][7], and the new [PHP password hashing][1] functions provide that level of\nsecurity.\n\nThe **Password Validator** library makes it (more) trivial to use the new\npassword hash functions in your application.  Just add the validator to your\nauthentication script and you're up and running.\n\nThe really big deal here is the **ease of upgrading** from your current legacy\nhashes to the new, more secure PHP password hash hashes.  Simply wrap the\n`PasswordValidator` in the `UpgradeDecorator`, provide a callback to validate\nyour existing password hashing scheme, and BOOM, you're using new password\nhashes in a manner *completely transparent* to your application's users. Nifty,\nhuh?\n\n## Usage\n\n### Password Validation\n\nIf you're already using [`password_hash`][2] generated passwords in your\napplication, you need do nothing more than add the validator in your\nauthentication script. The validator uses [`password_verify`][3] to test \nthe validity of the provided password hash.\n\n``` php\nuse JeremyKendall\\Password\\PasswordValidator;\n\n$validator = new PasswordValidator();\n$result = $validator-\u003eisValid($_POST['password'], $hashedPassword);\n\nif ($result-\u003eisValid()) {\n    // password is valid\n}\n```\n\nIf your application requires options other than the `password_hash` defaults,\nyou can set the `cost` option with `PasswordValidator::setOptions()`.\n\n``` php\n$options = array(\n    'cost' =\u003e 11\n);\n$validator-\u003esetOptions($options);\n```\n\n**IMPORTANT**: `PasswordValidator` uses a default cost of `10`. If your\nexisting hash implementation requires a different cost, make sure to specify it\nusing `PasswordValidator::setOptions()`. If you do not do so, all of your\npasswords will be rehashed using a cost of `10`.\n\n### Rehashing\n\nEach valid password is tested using [`password_needs_rehash`][4]. If a rehash\nis necessary, the valid password is hashed using `password_hash` with the\nprovided options. The result code `Result::SUCCESS_PASSWORD_REHASHED` will be\nreturned from `Result::getCode()` and the new password hash is available via\n`Result::getPassword()`.\n\n``` php\nif ($result-\u003egetCode() === Result::SUCCESS_PASSWORD_REHASHED) {\n    $rehashedPassword = $result-\u003egetPassword();\n    // Persist rehashed password\n}\n```\n\n**IMPORTANT**: If the password has been rehashed, it's critical that you\npersist the updated password hash. Otherwise, what's the point, right?\n\n### Upgrading Legacy Passwords\n\nYou can use the `PasswordValidator` whether or not you're currently using\n`password_hash` generated passwords. The validator will transparently upgrade\nyour current legacy hashes to the new `password_hash` generated hashes as each\nuser logs in.  All you need to do is provide a validator callback for your\npassword hash and then [decorate][6] the validator with the `UpgradeDecorator`.\n\n``` php\nuse JeremyKendall\\Password\\Decorator\\UpgradeDecorator;\n\n// Example callback to validate a sha512 hashed password\n$callback = function ($password, $passwordHash, $salt) {\n    if (hash('sha512', $password . $salt) === $passwordHash) {\n        return true;\n    }\n\n    return false;\n};\n\n$validator = new UpgradeDecorator(new PasswordValidator(), $callback);\n$result = $validator-\u003eisValid('password', 'password-hash', 'legacy-salt');\n```\n\nThe `UpgradeDecorator` will validate a user's current password using the\nprovided callback.  If the user's password is valid, it will be hashed with\n`password_hash` and returned in the `Result` object, as above.\n\nAll password validation attempts will eventually pass through the\n`PasswordValidator`. This allows a password that has already been upgraded to\nbe properly validated, even when using the `UpgradeDecorator`.\n\n#### Alternate Upgrade Technique\n\nRather than upgrading each user's password as they log in, it's possible to\npreemptively rehash persisted legacy hashes all at once. `PasswordValidator`\nand the `UpgradeDecorator` can then be used to validate passwords against the\nrehashed legacy hashes, at which point the user's plain text password will be\nhashed with `password_hash`, completing the upgrade process.\n\nFor more information on this technique, please see Daniel Karp's \n[Rehashing Password Hashes][10] blog post, and review\n[`JeremyKendall\\Password\\Tests\\Decorator\\KarptoniteRehashUpgradeDecoratorTest`][11]\nto see a sample implementation. \n\n### Persisting Rehashed Passwords\n\nWhenever a validation attempt returns `Result::SUCCESS_PASSWORD_REHASHED`, it's\nimportant to persist the updated password hash.\n\n``` php\nif ($result-\u003egetCode() === Result::SUCCESS_PASSWORD_REHASHED) {\n    $rehashedPassword = $result-\u003egetPassword();\n    // Persist rehashed password\n}\n```\n\nWhile you can always perform the test and then update your user database\nmanually, if you choose to use the **Storage Decorator** all rehashed passwords\nwill be automatically persisted.\n\nThe Storage Decorator takes two constructor arguments: An instance of\n`PasswordValidatorInterface` and an instance of the\n`JeremyKendall\\Password\\Storage\\StorageInterface`.\n\n#### StorageInterface\n\nThe `StorageInterface` includes a single method, `updatePassword()`. A class\nhonoring the interface might look like this:\n\n``` php\n\u003c?php\n\nnamespace Example;\n\nuse JeremyKendall\\Password\\Storage\\StorageInterface;\n\nclass UserDao implements StorageInterface\n{\n    public function __construct(\\PDO $db)\n    {\n        $this-\u003edb = $db;\n    }\n\n    public function updatePassword($identity, $password)\n    {\n        $sql = 'UPDATE users SET password = :password WHERE username = :identity';\n        $stmt = $this-\u003edb-\u003eprepare($sql);\n        $stmt-\u003eexecute(array('password' =\u003e $password, 'identity' =\u003e $identity));\n    }\n}\n```\n\n#### Storage Decorator\n\nWith your `UserDao` in hand, you're ready to decorate a\n`PasswordValidatorInterface`.\n\n``` php\nuse Example\\UserDao;\nuse JeremyKendall\\Password\\Decorator\\StorageDecorator;\n\n$storage = new UserDao($db);\n$validator = new StorageDecorator(new PasswordValidator(), $storage);\n\n// If validation results in a rehash, the new password hash will be persisted\n$result = $validator-\u003eisValid('password', 'passwordHash', null, 'username');\n```\n\n**IMPORTANT**: You must pass the optional fourth argument (`$identity`) to\n`isValid()` when calling `StorageDecorator::isValid()`.  If you do not do so,\nthe `StorageDecorator` will throw an `IdentityMissingException`.\n\n#### Combining Storage Decorator with Upgrade Decorator\n\nIt is possible to chain decorators together thanks to the \n[Decorator Pattern](https://en.wikipedia.org/wiki/Decorator_pattern). A great way to use this is to combine the \n`StorageDecorator` and `UpgradeDecorator` together to first update a legacy hash and then save it. Doing so is very \nsimple - you just need to pass an instance of the `StorageDecorator` as a constructor argument to `UpgradeDecorator`:\n\n``` php\nuse Example\\UserDao;\nuse JeremyKendall\\Password\\Decorator\\StorageDecorator;\nuse JeremyKendall\\Password\\Decorator\\UpgradeDecorator;\n\n// Example callback to validate a sha512 hashed password\n$callback = function ($password, $passwordHash, $salt) {\n    if (hash('sha512', $password . $salt) === $passwordHash) {\n        return true;\n    }\n\n    return false;\n};\n\n$storage = new UserDao($db);\n$storageDecorator = new StorageDecorator(new PasswordValidator(), $storage);\n$validator = new UpgradeDecorator($storageDecorator, $callback);\n\n// If validation results in a rehash, the new password hash will be persisted\n$result = $validator-\u003eisValid('password', 'passwordHash', null, 'username');\n```\n\n\n### Validation Results\n\nEach validation attempt returns a `JeremyKendall\\Password\\Result` object. The\nobject provides some introspection into the status of the validation process.\n\n* `Result::isValid()` will return `true` if the attempt was successful\n* `Result::getCode()` will return one of three possible `int` codes:\n    * `Result::SUCCESS` if the validation attempt was successful\n    * `Result::SUCCESS_PASSWORD_REHASHED` if the attempt was successful and the password was rehashed\n    * `Result::FAILURE_PASSWORD_INVALID` if the attempt was unsuccessful\n* `Result::getPassword()` will return the rehashed password, but only if the password was rehashed\n\n### Database Schema Changes\n\nAs mentioned above, because this library uses the `PASSWORD_DEFAULT` algorithm,\nit's important your password field be `VARCHAR(255)` to account for future\nupdates to the default password hashing algorithm.\n\n## Helper Scripts\n\nAfter running `composer install`, there are two helper scripts available, both \nrelated to the password hash functions.\n\n### version-check\n\nIf you're not already running PHP 5.5+, you should run `version-check` to\nensure your version of PHP is capable of using password-compat, the userland\nimplementation of the PHP password hash functions.  Run `./vendor/bin/version-check`\nfrom the root of your project. The result of the script is pass/fail.\n\n### cost-check\n\nThe default `cost` used by `password_hash` is 10.  This may or may not be\nappropriate for your production hardware, and it's entirely likely you can use\na higher cost than the default. `cost-check` is based on the [finding a good\ncost][8] example in the PHP documentation. Simply run `./vendor/bin/cost-check` from the command line and an appropriate cost will be returned.\n\n**NOTE**: The default time target is 0.2 seconds.  You may choose a higher or lower \ntarget by passing a float argument to `cost-check`, like so:\n\n``` bash\n$ ./vendor/bin/cost-check 0.4\nAppropriate 'PASSWORD_DEFAULT' Cost Found:  13\n```\n\n## Installation\n\nThe only officially supported method of installation is via\n[Composer](http://getcomposer.org).\n\nRunning the following command will add the latest version of the library to your project:\n``` bash\n$ composer require jeremykendall/password-validator\n```\n\nYou can update to the latest version with this command:\n``` bash\n$ composer update jeremykendall/password-validator\n```\n\nIf you're not already using Composer in your project, add the autoloader to your project:\n\n``` php\n\u003c?php\n\nrequire_once '../vendor/autoload.php'\n```\n\nYou're now ready to begin using the Password Validator.\n\n## Contributing\n\nPull requests are *always* welcome. Please review the CONTRIBUTING.md document before\nsubmitting pull requests.\n\n[1]: http://www.php.net/manual/en/ref.password.php\n[2]: http://www.php.net/manual/en/function.password-hash.php\n[3]: http://www.php.net/manual/en/function.password-verify.php\n[4]: http://www.php.net/manual/en/function.password-needs-rehash.php\n[5]: https://github.com/ircmaxell/password_compat\n[6]: http://en.wikipedia.org/wiki/Decorator_pattern\n[7]: http://csiphp.com/blog/2012/02/16/encrypt-passwords-for-highest-level-of-security/\n[8]: http://php.net/password_hash#example-875\n[9]: http://jeremykendall.net/2014/01/04/php-password-hashing-a-dead-simple-implementation/\n[10]: http://karptonite.com/2014/05/11/rehashing-password-hashes/\n[11]: tests/JeremyKendall/Password/Tests/Decorator/KarptoniteRehashUpgradeDecoratorTest.php\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeremykendall%2Fpassword-validator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjeremykendall%2Fpassword-validator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeremykendall%2Fpassword-validator/lists"}