Coder Social home page Coder Social logo

password-hashing's Introduction

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:

  • You're intending to create a new account, or the password to an existing account is being changed.
  • password is the password for the new account, or the new password for an existing account.

Postconditions:

  • CreateHash() gives you a string which can be used with VerifyPassword() to check, in the future, if a password is the same as the password given to this call.

Obligations:

  • Store the string CreateHash() returns to you in a safe place. If an attacker can modify your hashes, they will be able to change them to, for instance, the hash of "1234", and then log in to any account. If an attacker can view your hashes, they can begin cracking them (by trying to guess-and-check passwords).

Exceptions:

  • CannotPerformOperationException: If this exception is thrown, it means something is wrong with the platform your code is running on, and it's not safe to create a hash. For example, if your system's random number generator doesn't work properly, this kind of exception will be thrown.

VerifyPassword(password, correctHash)

Preconditions:

  • Someone is logging in to a user account which has been created in the past.
  • password is the password provided by the person trying to log in.
  • correctHash is the hash of the account's correct password, made with CreateHash() when the account was created or when its password was last changed. Make sure you are providing the hash for the correct user account!
  • correctHash hasn't been seen by or changed by an attacker since it was created.

Postconditions:

  • True is returned if the password provided by the person logging in is correct. False is returned if not.

Obligations:

  • Make sure the correctHash you're giving is for the right account. If you give a hash for the wrong account, it would let someone log into Alice's account using Bob's password!

Exceptions:

  • CannotPerformOperationException: If this exception is thrown, it means something is wrong with the platform your code is running on, and for some reason it's not safe to verify a password on it.
  • InvalidHashException: The correctHash you gave was somehow corrupted. Note that some ways of corrupting a hash are impossible to detect, and their only symptom will be that VerifyPassword() will return false even though the correct password was given. So InvalidHashException is not guaranteed to be thrown if a hash has been changed, but if it is thrown then you can be sure that the hash was changed.

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:

  • PBKDF2_HASH_ALGORITHM: The hash function PBKDF2 uses. By default, it is SHA1 for compatibility across implementations, but you may change it to SHA256 if you don't care about compatibility. Although SHA1 has been cryptographically broken as a collision-resistant function, it is still perfectly safe for password storage with PBKDF2.

  • PBKDF2_ITERATIONS: The number of PBKDF2 iterations. By default, it is 32,000. To provide greater protection of passwords, at the expense of needing more processing power to validate passwords, increase the number of iterations. The number of iterations should not be decreased.

  • PBKDF2_SALT_BYTES: The number of bytes of salt. By default, 24 bytes, which is 192 bits. This is more than enough. This constant should not be changed.

  • PBKDF2_HASH_BYTES: The number of PBKDF2 output bytes. By default, 18 bytes, which is 144 bits. While it may seem useful to increase the number of output bytes, doing so can actually give an advantage to the attacker, as it introduces unnecessary (avoidable) slowness to the PBKDF2 computation. 144 bits was chosen because it is (1) Less than SHA1's 160-bit output (to avoid unnecessary PBKDF2 overhead), and (2) A multiple of 6 bits, so that the base64 encoding is optimal.

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:

  • algorithm is the name of the cryptographic hash function ("sha1").
  • iterations is the number of PBKDF2 iterations ("64000").
  • hashSize is the length, in bytes, of the hash field (after decoding).
  • salt is the salt, base64 encoded.
  • hash is the PBKDF2 output, base64 encoded. It must encode hashSize bytes.

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.

password-hashing's People

Contributors

asmith3006 avatar codelingobot avatar defuse avatar hafarooki avatar paragonie-scott avatar redragonx avatar sarciszewski avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

password-hashing's Issues

Tests / Travis-CI

Write a suite of unit tests and have them run automatically on Travis-CI.

State of the PHP module in compatible branch

Thanks for having the time to create something like this.

The PHP code in the compartible-versions can be used in production? Or should wait for the it to reach a certain tag? (although there are no tags right now)

Ruby module: ==

Is == sufficient in Ruby? Or should a timing safe comparison method be patched in?

Standardize Exceptions Across Implementations

A password validation can fail for more than one reason. Either the password is wrong, or the hash is invalid (malformed), or something even weirder is going on. In any case, client code should be able to distinguish (as best possible) between these scenarios. Create standard exception names for the various cases so that they are consistent across the different implementations.

Make the class-based PHP implementation the only one.

PHP has supported classes since version 4, so I don't ever see anyone needing one that isn't a class, and if they really need one, they can take responsibility for converting it. Let's not waste effort maintaining it.

Apply BSD license.

Apply the two-clause BSD license to all of code.

http://opensource.org/licenses/BSD-2-Clause

<?php
/* 
 * Copyright (c) 2013, Taylor Hornby
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, 
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 */
?>

Drop Non-Compatbile Code // v2.0

There should only be one version of all the code, all of which is compatible with each other. The reason they weren't compatible in the first place is because I could save a few microseconds by avoiding a base64 encode/decode by writing it in a different way for each language. Obviously, that's retarded, since compatibility is 1000x more important.

Incorrect comments

/**
 * Converts a byte array into a hexadecimal string.
 *
 * @param   array       the byte array to convert
 * @return              a length*2 character string encoding the byte array
 */
private static String toBase64(byte[] array)
{
    return DatatypeConverter.printBase64Binary(array);
}

lol

The simple login example does not work.

The example won't work because it assumes you have a SQLITE db already with tables set. It is depending on a deleted file that was removed during cleanup of the repo. We made a ticket for this file issue: #20

The example doesn't seem to include its own DB.php file. I don't know what's up with that, but it needs to be fixed as well.

Travis's RNGCryptoServiceProvider isn't IDisposable

PasswordHash.cs(46,24): error CS1674: `System.Security.Cryptography.RNGCryptoServiceProvider': type used in a using statement must be implicitly convertible to `System.IDisposable'

/usr/lib/mono/2.0/mscorlib.dll (Location of the symbol related to previous error)

Compilation failed: 1 error(s), 0 warnings

Cannot open assembly 'Test.exe': No such file or directory.

FAIL.

rm: cannot remove `Test.exe': No such file or directory

Increase default PBKDF2 iterations.

1000 iterations is too few. In practice, logins are infrequent, and if someone is really processing more than 10 logins per second they probably already have significant infrastructure. Let's increase the default iteration count to 32000, which takes 0.08 seconds (~12.5/logins/sec/core) on my system.

Check Java is hashing the entire character

https://docs.oracle.com/javase/7/docs/api/javax/crypto/spec/PBEKeySpec.html

"Different PBE mechanisms may consume different bits of each password character. For example, the PBE mechanism defined in PKCS #5 looks at only the low order 8 bits of each character, whereas PKCS #12 looks at all 16 bits of each character."

We need to verify that all of the character is being included in the hash calculation, not just the lower 8 bits. Write a unit test for this.

C# IDisposable thing

From email:

Just wanted to note that the classes RNGCryptoServiceProvider and Rfc2898DeriveBytes implement IDisposable because they're using the operating system's cryptography API which needs to be freed after use. I've changed your code to wrap those instances in a using() block.

Use "verifier" instead of "hash"

Using the word "hash" to refer to CreateHash's return value is a misnomer. It's not a hash. It's a bunch of different things, including the output of PBKDF2 which isn't a hash. We should standardize the codebase and documentation to use the term "verifier" for this string.

Javascript version

Any plans to have this ported to Node.js? Should be pretty easy, it has all the requirements out of the box (that means, on npm)

If it's not already in the makings, I could try to hack something together

Travis-CI Multi-Language Support

Look and see if Travis supports multiple languages (and their versions) being specified in the configuration. Ideally, we want to test compatibility between the cross product of all versions of one language with all versions of another.

Hashes can be truncated and still validate

The length parameter to PBKDF2 is determined, when validating a password, by taking the strlen() of the hash component of the saved hash. This means that if, say, the hash part is intentionally or accidentally truncated to 1 byte then the hash will only provide 8 bits of security. This should not be a problem in practice since hashes should never be truncated, but it might affect some users who use it with a broken database that likes to truncate things so it should be fixed.

  • Add a test case for this issue
  • Add a length field, then compare it to the length of the hash that's there and return false if the length isn't what it's expected to be. That way we can raise a different error in the future so the user can distinguish "Something is SERIOUSLY wrong, the database truncated a hash" from "The user got their password wrong but everything is fine."
  • Ensure the test passes.

Edit: Changed the second point above. It used to say add the length to the salt, but that's bad because: (1) It doesn't let the user distinguish the hash being broken in that way from the user getting their password wrong (really, it should be a fatal exception whereas passwords are gotten wrong every day), and (2) If the hash is truncated to one byte, it will be correct with 1/256 probability even with the wrong length anyway.

Add Some Examples

(I'll probably end up referencing this in a PR before the day is over.)

Let's include some examples (e.g. a simple login form, etc) that implement this class, to help visually-inclined developers better understand how to use it.

Possible timing attack due to JIT optimizations (C# implementation)?

There may be a timing attack against the “SlowEquals” implementation in C#.

Should slow equals be decorated with

[MethodImpl(MethodImplOptions.NoOptimization)]

From:

http://stackoverflow.com/questions/20448765/methodimplnooptimization-on-this-method-what-does-it-do-and-is-it-really-nes


If a "smart" compiler turned that function into something that returns false as soon as a mismatch was found, it could make code using this function vulnerable to a "timing attack"---an attacker could conceivably figure out where the first mismatch in a string was based on how long the function took to return.
This isn't just science fiction, actually, even though it may seem like it is. Even with the Internet in the way, you can take a whole bunch of samples and use some statistics to figure out what's going on if you have a guess that the implementation short-circuits.

V2.0 Security Audit

Just before we release v2.0, after all of the other tickets in the milestone have been closed, it should be audited by me and some other volunteers. Maybe we can set up a bug bounty.

(Maybe @sarciszewski will be interested).

Close the current tickets then switch to "security maintenance mode."

This library uses PBKDF2. There are much better options now. I'm going to finish all of the currently-open tickets, and then "abandon" it. By abandon, I mean: No new features, only fixes to bugs (especially security bugs) reported by users.

The only case where you want to use this library is if you really needed compatibility between languages. Otherwise, there are better options.

Make tests with Vagrant

Travis-CI can't test multiple languages in a single repository. We have to use something like Vagrant. Supersedes #26.

Constant time comparision isn't really constant time

Didn't check the other implementions, but only PHP, it's checking:

for($i = 0; $i < strlen($a) && $i < strlen($b); $i++) //...

When supplying a bigger password for $b (like 100000 in length), the function will stop at the length of $a, which is much smaller, which after sometime and persistence can actually discover the length of the pbkdf2 stored, defeating the purpose of a constant time comparision.

both values should be sha1-hashed and transformed to hex, then compared. they will always have the same length, thus avoiding the problem.

The implementation, IMHO, should be more like this https://github.com/pocesar/password-hashing/blob/true-constant-time/PasswordHash.js#L94-L111

Re-Create the Class PHP Implementation

In the old branch, there was a PHP implementation that was a class, instead of putting functions in the global namespace. That's better for 99% of users. Bring that back and make it compatible.

Use native PBKDF2 when available in PHP.

PHP now has a native implementation of PBKDF2. We should use it when available:

http://php.net/manual/en/function.hash-pbkdf2.php

Someone has already done this, but unfortunately their code is broken:

http://pastebin.com/f5PDq735

Make sure to benchmark it and check that it's not slower, though. Sadly, with PHP, that's a real possibility. The other language implementations are all already using native implementations.

We should keep an eye out for native scrypt implementations, too.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.