defuse / password-hashing

Password hashing code.
BSD 2-Clause "Simplified" License
859 stars 222 forks source link

Secure Password Storage v2.0

Build Status

This repository contains peer-reviewed libraries for password storage in PHP, C#, Ruby, and Java. Passwords are "hashed" with PBKDF2 (64,000 iterations of SHA1 by default) using a cryptographically-random salt. The implementations are compatible with each other, so you can, for instance, create a hash in PHP and then verify it in C#.

Should you use this code?

This code uses the PBKDF2 algorithm to protect passwords. Better technologies for protecting passwords exist today, like bcrypt, scrypt, or Argon2. Before using this code, you should try to find a well-reviewed and carefully-made implementation of one of those algorithms for the language that you are using. These algorithms are "memory hard," meaning that they don't just need a lot of CPU power to compute, they also require a lot of memory (unlike PBKDF2). By using a memory hard algorithm, your passwords will be better protected.

One thing you could do would be to use libsodium to hash your passwords with scrypt. It has bindings available for many languages. For PHP apps, a great option is to use the built-in password_hash() and password_verify() functions.

Since there are better options, this code is now in "maintenance mode." Only bugs will be fixed, no new features will be added. It is currently safe to use, but using libsodium would be better.

Usage

You should not store users' passwords in plain text on your servers. Nor should you even store them in encrypted form. The correct way to store a password is to store something created from the password, which we'll call a "hash." Hashes don't allow you to recover the password, they only let you check if a password is the same as the one that created the hash.

There are a lot of subtle details about password hashing that this library hides from you. You don't need to worry about things like "salt" with this library. It takes care of all of that for you.

To implement a user login system, you need two parts: creating new accounts, and logging in to existing accounts. When you create a new account, your code will create a hash of the new account's password and save it somewhere. When you log in to an account, your code will use the hash to check if the login password is correct.

To create a hash, when a new account is added to your system, you call the CreateHash() method provided by this library. To verify a password, you call VerifyPassword() method provided by this library.

Here is more specific documentation for both functions. The behavior should be the same for all of the implementations (although the method names differ slightly). If one implementation behaves differently than another, that is a bug, and should be filed in the GitHub issue tracker.

CreateHash(password)

Preconditions:

Postconditions:

Obligations:

Exceptions:

VerifyPassword(password, correctHash)

Preconditions:

Postconditions:

Obligations:

Exceptions:

Customization

Each implementation provides several constants that can be changed. Only change these if you know what you are doing, and have help from an expert:

Note that these constants are encoded into the hash string when it is created with CreateHash so that they can be changed without breaking existing hashes. The new (changed) values will apply only to newly-created hashes.

Hash Format

The hash format is five fields separated by the colon (':') character.

algorithm:iterations:hashSize:salt:hash

Where:

Here are some example hashes (all of the password "foobar"):

sha1:64000:18:B6oWbvtHvu8qCgoE75wxmvpidRnGzGFt:R1gkPOuVjqIoTulWP1TABS0H
sha1:64000:18:/GO9XQOPexBFVzRjC9mcOkVEi7ZHQc0/:0mY83V5PvmkkHRR41R1iIhx/
sha1:64000:18:rxGkJ9fMTNU7ezyWWqS7QBOeYKNUcVYL:tn+Zr/xo99LI+kSwLOUav72X
sha1:64000:18:lFtd+Qf93yfMyP6chCxJP5nkOxri6Zbh:B0awZ9cDJCTdfxUVwVqO+Mb5

The hash length in bytes is included to prevent an accident where the hash gets truncated. For instance, if the hash were stored in a database column that wasn't big enough, and the database was configured to truncate it, the result when the hash gets read back would be an easy-to-break hash, since the PBKDF2 output is right at the end. Therefore, the length of the hash should not be determined solely from the length of the last field; it must be compared against the stored length.

More Information

For more information on secure password storage, see Crackstation's page on Password Hashing Security.