If you are unfamiliar with cryptography concepts or the vocabulary it uses, or especially you are looking for guidance on "password encryption", please read this page first.
We've previously said that even security advice should carry an expiration date. So unlike most of our past blog posts, this page should be considered a living document: As requirements change and new attacks are discovered, we will update it accordingly. A changelog is at the end of the document.
Semantic point: Don't store the password, store a hash of the password. (Obligatory.)
Modern, Secure, Salted Password Hashing Made Simple
The Problem: You want people to be able to create a unique user account, with a password, which they will use to access your application. How can you safely implement this feature?
Easiest Solution: Use libsodium, which provides a secure password hashing API in most languages. As of version 1.0.9, libsodium delivers Argon2, the most recent, carefully-selected algorithm from the Password Hashing Competition. Libsodium offers bindings for most programming languages.
Note: There was a published attack on an earlier version of Argon2i, the recommended variant of Argon2 for general purpose password hashing. The practical implications weren't severe; if you're using Argon2i version 1.3 or higher (with at least 3 passes) you're safe. Libsodium does this.
If you, for whatever reason, cannot reconcile your requirements with installing libsodium, you have other options. In preparing this blog post, our security team has investigated several password hashing libraries in multiple programming languages. What follows is our current recommendations for secure password storage with example code.
Acceptable Password Hashing Algorithms
Although there is disagreement about how to rank them, cryptography experts agree that these algorithms are the only ones you should be using to store passwords in 2016:
- Argon2, the Password Hashing Competition winner.
- bcrypt
- scrypt
- The other Password Hashing Competition finalists (Catena, Lyra2, Makwa, and yescrypt)
- PBKDF2 (nearly everyone except FIPS agrees PBKDF2 is the worst of the acceptable options, but is still acceptable)
Secure Password Storage in PHP
If you want to upgrade to Argon2i before PHP's password hashing API catches up, the only way to do so is through libsodium. Refer to the relevant section of the documentation for instructions on installing libsodium for PHP.
// Password hashing:
$hash_str = \Sodium\crypto_pwhash_str(
$password,
\Sodium\CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
\Sodium\CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);
// Password verification:
if (\Sodium\crypto_pwhash_str_verify($hash_str, $password)) {
// recommended: wipe the plaintext password from memory
\Sodium\memzero($password);
// Password was valid.
} else {
// recommended: wipe the plaintext password from memory
\Sodium\memzero($password);
// Password was invalid.
}
If you're using Halite (our developer-friendly FOSS PHP libsodium wrapper), the Password
class uses Argon2i since version 2.0.0.
<?php
use ParagonIE\Halite\{
KeyFactory,
Password
};
// Load the encryption key:
$key = KeyFactory::loadEncryptionKey('/path/to/encryption_key');
// Calculate the encrypted password hash:
$hash = Password::hash($password, $key);
// Verify the given password against the encrypted password hash:
if (Password::verify($password, $hash, $key)) {
// Login successful
}
Alternative: Bcrypt Password Hashing in PHP
First, make sure you're using a supported version of PHP. If you are, then the PHP password API will be available for use. If you aren't, consider upgrading. If you can't, check out password_compat.
$hash = password_hash($userPassword, PASSWORD_DEFAULT);
password_hash()
takes care of salting the hash, transparently. You can, however, specify your own cost. The absolute minimum value you should consider using is 10
. 12
is good, provided your hardware supports it. The default cost parameter is 10
.
$hash = password_hash($userPassword, PASSWORD_DEFAULT, ['cost' => 12]);
Verifying a password against a stored hash is incredibly simple:
if (password_verify($userPassword, $hash)) {
// Login successful.
if (password_needs_rehash($hash, PASSWORD_DEFAULT, ['cost' => 12])) {
// Recalculate a new password_hash() and overwrite the one we stored previously
}
}
As of PHP 7, PASSWORD_DEFAULT
still uses bcrypt. In a future version, it may migrate to Argon2.
Alternative: Scrypt Password Hashing in PHP
If you aren't using libsodium (which we strongly recommend that you do use!), you can still get scrypt hashes in PHP through Dominic Black's Scrypt PHP Extension from PECL.
# If you don't have PECL installed, get that first.
pecl install scrypt
echo "extension=scrypt.so" > /etc/php5/mods-available/scrypt.ini
php5enmod scrypt
Next, grab a copy of the bundled PHP wrapper and include it in your project.
# Hashing
$hash = \Password::hash($userProvidedPassword);
# Validation
if (\Password::check($userProvidedPassword, $hash)) {
// Logged in successfully.
}
Secure Password Storage in Java
There is a Java implementation of Argon2 outside of libsodium, but it requires you to specify the parameters rather than providing sane defaults.
// Create instance
Argon2 argon2 = Argon2Factory.create();
// Read password from user
char[] passwd = readPasswordFromUser();
try {
int N = 65536;
int r = 2;
int p = 1;
// Hash password
String hashed = argon2.hash(r, N, p, passwd);
// Validating a hash
if (argon2.verify(hash, passwd)) {
// Login successful
}
} finally {
// Wipe confidential data
argon2.wipeArray(passwd);
}
Alternative: Bcrypt Password Hashing in Java
jBCrypt provides the bcrypt password hashing algorithm.
String hash = BCrypt.hashpw(userProvidedPassword, BCrypt.gensalt());
Verifying a bcrypt hash in Java:
if (BCrypt.checkpw(userProvidedPassword, hash)) {
// Login successful.
}
Alternative: Scrypt Password Hashing in Java
There is a Java implementation of scrypt, but it requires you to specify the parameters rather than providing sane defaults.
# Calculating a hash
int N = 16384;
int r = 8;
int p = 1;
String hashed = SCryptUtil.scrypt(passwd, N, r, p);
# Validating a hash
if (SCryptUtil.check(passwd, hashed)) {
// Login successful
}
Secure Password Storage in C# (.NET)
Argon2 is available for .NET developers through the .NET bindings for libsodium.
// Hashing a password
var hash = PasswordHash.ArgonHashString(yourPasswordString, Strength.Interactive)
if (PasswordHash.ArgonHashStringVerify(hash, yourPasswordString) {
//correct password
}
Alternative: Bcrypt Secure Password Storage in C# (.NET)
We recommend Chris McKee's Bcrypt.NET over System.Security.Cryptography.Rfc2898DeriveBytes
, which is PBKDF2-SHA1. (We're not saying PBKDF2-SHA1 is unsafe, but that bcrypt is preferable to it.)
// Calculating a hash
string hash = BCrypt.HashPassword(usersPassword, BCrypt.GenerateSalt());
// Validating a hash
if (BCrypt.Verify(usersPassword, hash)) {
// Login successful
}
Alternative: Scrypt Password Hashing in C# (.NET)
There is an Scrypt package in NuGET as well.
// This is necessary:
ScryptEncoder encoder = new ScryptEncoder();
// Calculating a hash
SecureString hashedPassword = encoder.Encode(usersPassword);
// Validating a hash
if (encoder.Compare(usersPassword, hashedPassword)) {
// Login successful
}
Secure Password Storage in Ruby
There is a Ruby Argon2 gem, which is rather straightforward to use:
hasher = Argon2::Password.new
hashed_password = hasher.create("password")
if Argon2::Password.verify_password("password", hashed_password)
# Login successful
Alternative: Bcrypt Hashing in Ruby
From the author of "Use bcrypt. Use bcrypt. Use bcrypt..." comes a Ruby gem for bcrypt password hashing.
require "bcrypt"
# Calculating a hash
my_password = BCrypt::Password.create(usersPassword)
# Validating a hash
if my_password == usersPassword
# Login successful
Beware that, as of this writing, this library is not following cryptography coding best practices. Consider applying this patch until they get around to merging it.
Alternative: Scrypt Hashing in Ruby
There is also a Ruby gem for scrypt password hashing.
require "scrypt"
# Calculating a hash
password = SCrypt::Password.create(usersPassword)
# Validating a hash
if password == usersPassword
# Login successful
Secure Password Storage in Python
Python developers generally prefer passlib (Bitbucket). Since version 1.7.0, offers Argon2 support.
from passlib.hash import argon2
# Calculating a hash
hash = argon2.using(rounds=4).hash(usersPassword)
# Validating a hash
if argon2.verify(usersPassword, hash):
# Login successful
Alternative: Bcrypt Hashing in Python
Passlib also offers bcrypt password hashing. Before version 1.7.0, however, it was called bcrypt.encrypt()
rather than bcrypt.hash()
.
from passlib.hash import bcrypt
# Calculating a hash
hash = bcrypt.hash(usersPassword, rounds=12)
# Misnomer, but that's what it was called before v1.7.0:
# hash = bcrypt.encrypt(usersPassword, rounds=12)
# Validating a hash
if bcrypt.verify(usersPassword, hash):
# Login successful
Alternatively, you can't go wrong with the bcrypt Python package (also on github):
import bcrypt
import hmac
# Calculating a hash
password = b"correct horse battery staple"
hashed = bcrypt.hashpw(password, bcrypt.gensalt())
# Validating a hash (don't use ==)
if (hmac.compare_digest(bcrypt.hashpw(password, hashed), hashed)):
# Login successful
Alternative: Scrypt Hashing in Python
Passlib, since version 1.7.0, offers Scrypt support.
from passlib.hash import scrypt
# Calculating a hash
hash = scrypt.using(rounds=8).hash(usersPassword)
# Validating a hash
if scrypt.verify(usersPassword, hash):
# Login successful
Secure Password Storage in Node.js
There are other implementations of Argon2 available in the Node.js ecosystem, but you're better off using node-sodium, which handles salt generation for you.
// notice the .api part; that's important
var sodium = require('sodium').api;
var hash = new Buffer(sodium.crypto_pwhash_STRBYTES);
var passwordBuffer = new Buffer("password goes here");
hash = sodium.crypto_pwhash_str(
passwordBuffer,
sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE
);
// Validation:
if (sodium.crypto_pwhash_str_verify(
hash,
passwordBuffer
)) {
// You are logged in
}
Alternative: Bcrypt Password Hashing in Node.js
There are two secure implementations of bcrypt in Node.js, although bcrypt (Github) seems to be the preferred one. We're making extensive use of Promises to greatly simplify error handling in the examples below:
var Promise = require("bluebird");
var bcrypt = Promise.promisifyAll(require("bcrypt"));
function addBcryptType(err) {
// Compensate for `bcrypt` not using identifiable error types
err.type = "bcryptError";
throw err;
}
// Calculating a hash:
Promise.try(function() {
return bcrypt.hashAsync(usersPassword, 10).catch(addBcryptType);
}).then(function(hash) {
// Store hash in your password DB.
});
// Validating a hash:
// Load hash from your password DB.
Promise.try(function() {
return bcrypt.compareAsync(usersPassword, hash).catch(addBcryptType);
}).then(function(valid) {
if (valid) {
// Login successful
} else {
// Login wrong
}
});
// You would handle errors something like this, but only at the top-most point where it makes sense to do so:
Promise.try(function() {
// Generate or compare a hash here
}).then(function(result) {
// ... some other stuff ...
}).catch({type: "bcryptError"}, function(err) {
// Something went wrong with bcrypt
});
Alternative: Scrypt Password Hashing in Node.js
We recommend scrypt for humans (Github), a developer-friendly node-scrypt wrapper that's easy to use and hard to misuse.
var Promise = require("bluebird");
var scrypt = require("scrypt-for-humans");
// Calculating a hash:
Promise.try(function() {
return scrypt.hash(usersPassword);
}).then(function(hash) {
// Store hash for long term use
});
// Validating a hash:
Promise.try(function() {
return scrypt.verifyHash(usersPassword, hash);
}).then(function() {
// Login successful
}).catch(scrypt.PasswordError, function(err) {
// Login failed
});
(Example code made better by Sven Slootweg, the scrypt-for-humans maintainer.)
Frequently Asked Questions
Where's PBKDF2?
Although PBKDF2 is more widely available than bcrypt or scrypt, it doesn't offer the GPU resistance that we need from a password hashing function. If you must use PBKDF2, make sure you use at least 100,000
iterations and a SHA2 family hash function.
To reiterate: PBKDF2 can still be secure. It's the least secure of the acceptable password hashing algorithms on this page, so we aren't going to provide any example code.
Why prioritize bcrypt over scrypt?
On a technical level, they're vastly different, but for practical purposes they're morally equivalent. The real weakness is in not using an acceptable password hashing function at all. If you can use either of these two, use it. They're both fine.
Our choice for bcrypt as the default was simply: In PHP (which represents a little over 80% of the Internet), the easiest choice for developers to implement in their applications is bcrypt (via the password hashing API that shipped with PHP 5.5). Using scrypt requires root access and the ability to install PHP extensions via PECL.
Why choose scrypt over bcrypt:
- Bcrypt truncates after 72 characters.
- Bcrypt truncates on
NUL
bytes. - (These aren't bugs in the PHP implementation. They're bugs in the algorithm itself.)
Why choose bcrypt over scrypt:
- Scrypt's memory hardness isn't perfect
- Scrypt requires about 1000 times the memory as bcrypt for the same security against GPU attacks
If you have to choose between the two for password hashing, they're both good options. If you choose bcrypt, however, passing a base64-encoded SHA-384 hash to bcrypt is probably a good move:
-
base64_encode(hash('sha384', $password, true))
is 64 characters, which nearly fills up the 72 character keyspace - A base64-encoded hash is guaranteed to not contain
NUL
bytes - Two passwords with the same 72 character prefix but differ after the 73rd character will, with overwhelming likelihood, produce different SHA-384 hashes
- SHA-384 has about 192 bits of birthday collision resistance
- SHA-384 is also the largest SHA-2 family hash function that is resistant to length extension attacks
The above construction may invite theoretical concerns about entropy reduction (i.e. 72 characters of raw binary without any NUL
bytes comes out to about 573 bits of possible entropy, but a SHA-384 hash outputs are clearly limited to 384 bits).
A birthday SHA-384 collision still requires $2^{192}$ guesses. $2^{192}$ is large enough to fall under boring cryptography, barring any new attacks.
I'm not using bcrypt/scrypt. How should I migrate my legacy hashes?
The most straightforward approach to migrating your legacy hashes from, e.g. MD5, to bcrypt/scrypt is to follow this strategy (which was first introduced to us in a Reddit discussion by NeoThermic):
- Add a column to your user accounts table, called
legacy_password
(or equivalent). - Calculate the bcrypt/scrypt/Argon2 hash of the existing password hashes and store them in the database.
- Modify your authentication code to handle the legacy flag.
When a user attempts to login, first check if the legacy_password
flag is set. If it is, first pre-hash their password with your old password hashing algorithm, then use this prehashed value in place of their password. Afterwards, recalculate the bcrypt hash and store the new hash in the database, disabling the legacy_password
flag in the process. A very loose example in PHP 7+ follows:
/**
* This is example code. Please feel free to use it for reference but don't just copy/paste it.
*
* @param string $username Unsafe user-supplied data: The username
* @param string $password Unsafe user-supplied data: The password
* @return int The primary key for that user account
* @throws InvalidUserCredentialsException
*/
public function authenticate(string $username, string $password): int
{
// Database lookup
$stmt = $this->db->prepare(
"SELECT
userid, passwordhash, legacy_password
FROM
user_accounts
WHERE
username = ?"
);
$stmt->execute([$username]);
$stored = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$stored) {
// No such user, throw an exception
throw new InvalidUserCredentialsException();
}
if ($stored['legacy_password']) {
// This is the legacy password upgrade code
if (password_verify(legacy_hashing_algorithm($password), $stored['passwordhash'])) {
$newHash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $this->db
->prepare(
"UPDATE
user_accounts
SET
passwordhash = ?,
legacy_password = FALSE
WHERE
userid = ?"
)->execute([
$newHash,
$stored['userid']
]);
// Return the user ID (integer)
return $stored['userid'];
}
} elseif (password_verify($password, $stored['passwordhash'])) {
// This is the general purpose upgrade code e.g. if a future version of PHP upgrades to Argon2
if (password_needs_rehash($stored['passwordhash'], PASSWORD_DEFAULT)) {
$newhash = password_hash($password, PASSWORD_DEFAULT);
$this->db
->prepare("UPDATE user_accounts SET passwordhash = ? WHERE userid = ?")
->execute([$newhash, $stored['userid']]);
}
// Return the user ID (integer)
return $stored['userid'];
}
// When all else fails, throw an exception
throw new InvalidUserCredentialsException();
}
Usage:
try {
$userid = $this->authenticate($username, $password);
// Update the session state
// Redirect to the post-authentication landing page
} catch (InvalidUserCredentialsException $e) {
// Log the failure
// Redirect to the login form
}
Proactively upgrading legacy hashes is a security win over an opportunistic strategy (rehashing when the user logs in, but leave the insecure hashes in the database for inactive users): With a proactive strategy, if your server gets compromised before everyone logs in again, their passwords are already using an acceptable algorithm (bcrypt, in the example code).
The above example code is also available in Bcrypt-SHA-384 flavor.
What about password managers?
Password managers are a different beast. When we say "don't encrypt passwords", we're talking about server-side password validation. You should definitely use a password manager.
How do I implement secure server-side password storage in (other language)?
If you'd like us to consider another programming language for inclusion on this page, please reach out to our security@
team directly and we'll let popular demand guide our investigations.
I followed the advice on this page, but how can I be sure I did it correctly?
Check the library you're using; it should provide test vectors. If all else fails: We audit code.
Changelog
- 2017-03-19 - Recommend a better (and maintained) Bcrypt implementation for .NET projects.
- 2016-12-13 - We now recommend Argon2 as the go-to algorithm of choice, and demoted bcrypt to an alternative.
- 2016-05-16 - Argon2 v1.3 has matured and landed in libsodium. We've updated our recommendations for each language to explain how to get Argon2i as an alternative. Once it's widely adopted, we will make it the primary recommendation. Specifically covered: