{"id":13717891,"url":"https://github.com/defuse/password-hashing","last_synced_at":"2025-04-07T21:08:14.181Z","repository":{"id":8242496,"uuid":"9686779","full_name":"defuse/password-hashing","owner":"defuse","description":"Password hashing code.","archived":false,"fork":false,"pushed_at":"2022-08-24T15:59:00.000Z","size":149,"stargazers_count":857,"open_issues_count":8,"forks_count":219,"subscribers_count":80,"default_branch":"master","last_synced_at":"2024-10-14T12:09:51.778Z","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":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/defuse.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-04-26T03:39:32.000Z","updated_at":"2024-10-03T05:11:22.000Z","dependencies_parsed_at":"2022-08-06T23:15:28.554Z","dependency_job_id":null,"html_url":"https://github.com/defuse/password-hashing","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defuse%2Fpassword-hashing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defuse%2Fpassword-hashing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defuse%2Fpassword-hashing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/defuse%2Fpassword-hashing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/defuse","download_url":"https://codeload.github.com/defuse/password-hashing/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247730068,"owners_count":20986404,"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-03T00:01:28.763Z","updated_at":"2025-04-07T21:08:14.149Z","avatar_url":"https://github.com/defuse.png","language":"PHP","funding_links":[],"categories":["PHP"],"sub_categories":[],"readme":"Secure Password Storage v2.0\n=============================\n\n[![Build Status](https://travis-ci.org/defuse/password-hashing.svg?branch=master)](https://travis-ci.org/defuse/password-hashing)\n\nThis repository contains peer-reviewed libraries for password storage in PHP,\nC#, Ruby, and Java. Passwords are \"hashed\" with PBKDF2 (64,000 iterations of\nSHA1 by default) using a cryptographically-random salt. The implementations are\ncompatible with each other, so you can, for instance, create a hash in PHP and\nthen verify it in C#.\n\nShould you use this code?\n--------------------------\n\nThis code uses the PBKDF2 algorithm to protect passwords. Better technologies\nfor protecting passwords exist today, like bcrypt, scrypt, or Argon2. Before\nusing this code, you should try to find a well-reviewed and carefully-made\nimplementation of one of those algorithms for the language that you are using.\nThese algorithms are \"memory hard,\" meaning that they don't just need a lot of\nCPU power to compute, they also require a lot of memory (unlike PBKDF2). By\nusing a memory hard algorithm, your passwords will be better protected.\n\nOne thing you could do would be to use\n[libsodium](https://github.com/jedisct1/libsodium) to [hash your passwords with\nscrypt](https://download.libsodium.org/doc/password_hashing/index.html). It has\nbindings available for many languages. For PHP apps, a great option is to use the\nbuilt-in\n[`password_hash()`](https://secure.php.net/manual/en/function.password-hash.php)\nand\n[`password_verify()`](https://secure.php.net/manual/en/function.password-verify.php) functions.\n\nSince there are better options, this code is now in \"maintenance mode.\" Only\nbugs will be fixed, no new features will be added. It is currently safe to use,\nbut using libsodium would be better.\n\nUsage\n------\n\nYou should not store users' passwords in plain text on your servers. Nor should\nyou even store them in encrypted form. The correct way to store a password is to\nstore something created from the password, which we'll call a \"hash.\" Hashes\ndon't allow you to recover the password, they only let you check if a password\nis the same as the one that created the hash.\n\nThere are a lot of subtle details about password hashing that this library hides\nfrom you. You don't need to worry about things like \"salt\" with this library. It\ntakes care of all of that for you.\n\nTo implement a user login system, you need two parts: creating new accounts, and\nlogging in to existing accounts. When you create a new account, your code will\ncreate a hash of the new account's password and save it somewhere. When you log\nin to an account, your code will use the hash to check if the login password is\ncorrect.\n\nTo create a hash, when a new account is added to your system, you call the\n`CreateHash()` method provided by this library. To verify a password, you call\n`VerifyPassword()` method provided by this library.\n\nHere is more specific documentation for both functions. The behavior should be\nthe same for all of the implementations (although the method names differ\nslightly). If one implementation behaves differently than another, that is\na bug, and should be filed in the GitHub issue tracker.\n\n### CreateHash(password)\n\n**Preconditions:**\n\n- You're intending to create a new account, or the password to an existing\n  account is being changed.\n- `password` is the password for the new account, or the new password for an\n  existing account.\n\n**Postconditions:**\n\n- `CreateHash()` gives you a string which can be used with `VerifyPassword()` to\n  check, in the future, if a password is the same as the `password` given to\n  this call.\n\n**Obligations:**\n\n- Store the string `CreateHash()` returns to you in a safe place. If an attacker\n  can modify your hashes, they will be able to change them to, for instance, the\n  hash of \"1234\", and then log in to any account. If an attacker can view your\n  hashes, they can begin cracking them (by trying to guess-and-check passwords).\n\n**Exceptions:**\n\n- `CannotPerformOperationException`: If this exception is thrown, it means\n  something is wrong with the platform your code is running on, and it's not\n  safe to create a hash. For example, if your system's random number generator\n  doesn't work properly, this kind of exception will be thrown.\n\n### VerifyPassword(password, correctHash)\n\n**Preconditions:**\n\n- Someone is logging in to a user account which has been created in the past.\n- `password` is the password provided by the person trying to log in.\n- `correctHash` is the hash of the account's correct password, made with\n  `CreateHash()` when the account was created or when its password was last\n  changed. Make sure you are providing the hash for the correct user account!\n- `correctHash` hasn't been seen by or changed by an attacker since it was\n  created.\n\n**Postconditions:**\n\n- True is returned if the password provided by the person logging in is correct.\n  False is returned if not.\n\n**Obligations:**\n\n- Make sure the `correctHash` you're giving is for the right account. If you\n  give a hash for the wrong account, it would let someone log into Alice's\n  account using Bob's password!\n\n**Exceptions:**\n\n- `CannotPerformOperationException`: If this exception is thrown, it means\n  something is wrong with the platform your code is running on, and for some\n  reason it's not safe to verify a password on it.\n- `InvalidHashException`: The `correctHash` you gave was somehow corrupted. Note\n  that some ways of corrupting a hash are impossible to detect, and their only\n  symptom will be that `VerifyPassword()` will return false even though the\n  correct password was given. So `InvalidHashException` is not guaranteed to be\n  thrown if a hash has been changed, but *if it is thrown* then you can be sure\n  that the hash was changed.\n\nCustomization\n--------------\n\nEach implementation provides several constants that can be changed. **Only\nchange these if you know what you are doing, and have help from an expert**:\n\n- `PBKDF2_HASH_ALGORITHM`: The hash function PBKDF2 uses. By default, it is SHA1\n  for compatibility across implementations, but you may change it to SHA256 if\n  you don't care about compatibility. Although SHA1 has been cryptographically\n  broken as a collision-resistant function, it is still perfectly safe for\n  password storage with PBKDF2.\n\n- `PBKDF2_ITERATIONS`: The number of PBKDF2 iterations. By default, it is\n  32,000. To provide greater protection of passwords, at the expense of needing\n  more processing power to validate passwords, increase the number of\n  iterations. The number of iterations should not be decreased.\n\n- `PBKDF2_SALT_BYTES`: The number of bytes of salt. By default, 24 bytes, which\n  is 192 bits. This is more than enough. This constant should not be changed.\n\n- `PBKDF2_HASH_BYTES`: The number of PBKDF2 output bytes. By default, 18 bytes,\n  which is 144 bits. While it may seem useful to increase the number of output\n  bytes, doing so can actually give an advantage to the attacker, as it\n  introduces unnecessary (avoidable) slowness to the PBKDF2 computation. 144\n  bits was chosen because it is (1) Less than SHA1's 160-bit output (to avoid\n  unnecessary PBKDF2 overhead), and (2) A multiple of 6 bits, so that the base64\n  encoding is optimal.\n\nNote that these constants are encoded into the hash string when it is created\nwith `CreateHash` so that they can be changed without breaking existing hashes.\nThe new (changed) values will apply only to newly-created hashes.\n\nHash Format\n------------\n\nThe hash format is five fields separated by the colon (':') character.\n\n```\nalgorithm:iterations:hashSize:salt:hash\n```\n\nWhere:\n\n- `algorithm` is the name of the cryptographic hash function (\"sha1\").\n- `iterations` is the number of PBKDF2 iterations (\"64000\").\n- `hashSize` is the length, in bytes, of the `hash` field (after decoding).\n- `salt` is the salt, base64 encoded.\n- `hash` is the PBKDF2 output, base64 encoded. It must encode `hashSize` bytes.\n\nHere are some example hashes (all of the password \"foobar\"):\n\n```\nsha1:64000:18:B6oWbvtHvu8qCgoE75wxmvpidRnGzGFt:R1gkPOuVjqIoTulWP1TABS0H\nsha1:64000:18:/GO9XQOPexBFVzRjC9mcOkVEi7ZHQc0/:0mY83V5PvmkkHRR41R1iIhx/\nsha1:64000:18:rxGkJ9fMTNU7ezyWWqS7QBOeYKNUcVYL:tn+Zr/xo99LI+kSwLOUav72X\nsha1:64000:18:lFtd+Qf93yfMyP6chCxJP5nkOxri6Zbh:B0awZ9cDJCTdfxUVwVqO+Mb5\n```\n\nThe hash length in bytes is included to prevent an accident where the hash gets\ntruncated. For instance, if the hash were stored in a database column that\nwasn't big enough, and the database was configured to truncate it, the result\nwhen the hash gets read back would be an easy-to-break hash, since the PBKDF2\noutput is right at the end. Therefore, the length of the hash should not be\ndetermined solely from the length of the last field; it must be compared against\nthe stored length.\n\nMore Information\n-----------------\n\nFor more information on secure password storage, see [Crackstation's page on\nPassword Hashing Security](https://crackstation.net/hashing-security.htm).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdefuse%2Fpassword-hashing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdefuse%2Fpassword-hashing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdefuse%2Fpassword-hashing/lists"}