Development Security Guide: How to securely store user passwords 0 × 00 preface
First, if you are not familiar with the concept and use of cryptography, or you are in need of some cryptographic guidance, I recommend that you read this article.
Previously, we made it clear that even security recommendations should have a shelf life. Therefore, unlike most blog posts we published in the past, the content above is actually in a "update at any time" status: when security requirements change and new forms of attacks are discovered, we will make corresponding changes.
Here we propose a password security point: do not store the plaintext password, but store the hash value of the password.
In fact, it is very simple to generate a secure password hash value.
However, there is a problem here: you may want someone else to set up a password-based account. with this account and password, someone else can log on to your program, how can this function be securely implemented?
It's easy to solve this problem-use libsodium. It provides a secure password hash API for most languages. Earlier than Version 1.0.8, it used the scrypt algorithm, but from version 1.0.9, it also provided Argon2, an algorithm carefully selected from the recent hash password comparison. Libsodium provides binding to most programming languages.
Libsodium file
Libsodium source code
Note: an attack on Argon2i-a variant of Argon2 universal password hash is published here. In fact, the impact is not serious, but it may lead to a new variant (maybe Argon2x, because it may use XOR instead of overwriting the memory to mitigate these attacks ).
If you cannot reconcile libsodium with your requirements for various reasons, you have other options. When preparing this blog, our team has studied several cryptographic hash libraries in multiple programming languages. The following describes how to use the sample code.
Currently, the following cryptographic hashing algorithms are supported:
Champion of Argon2 password hash competition
Bcrypt
Scrypt
Other algorithms used in the password hash competition (Catena, Lyra2, Makwa, and yescrypt)
PBKDF2 is the worst choice
0 × 01 first PHP solution
First, check whether your version supports PHP. If yes, the PHP Password API can be used. If not, try upgrading. if not, check password_compat.
$hash = password_hash($userPassword, PASSWORD_DEFAULT);
Apparently password_hash () uses the hash value calculated by adding salt. Adjust the value based on your actual situation. if the hardware supports the value, it is good to use the minimum absolute value of 10.12. the default value is 10.
$hash = password_hash($userPassword, PASSWORD_DEFAULT, ['cost' => 12]);
In fact, it is very easy to verify the hash storage of a password:
if (password_verify($userPassword, $hash)) { // Login successful. if (password_needs_rehash($userPassword, PASSWORD_DEFAULT, ['cost' => 12])) { // Recalculate a new password_hash() and overwrite the one we stored previously }}
In PHP7, PASSWORD_DEFAULT still uses bcrypt. In future versions, it may gradually become Argon2.
PHP solution 2
If you cannot use libsodium (we strongly recommend that you use it), you can still use PHP to encrypt the hash by using the Scrypt PHP extension from Dominic Black of PECL.
#If you don't have PECL installed, get that first.pecl install scryptecho "extension=scrypt.so" > /etc/php5/mods-available/scrypt.iniphp5enmod scrypt
Next, bind a PHP package to your project.
# Hashing$hash = \Password::hash($userProvidedPassword);# Validationif (\Password::check($userProvidedPassword, $hash)) { // Logged in successfully.}
0 × 02 first JAVA solution
Implements secure password hashing in Java programs. besides libsodium and jBCrypt, it provides the bcrypt hashing algorithm.
String hash = BCrypt.hashpw(userProvidedPassword, BCrypt.gensalt());
Verify a bcrypt hash in Java:
if (BCrypt.checkpw(userProvidedPassword, hash)) { // Login successful.}
JAVA solution 2
There is a scrypt implemented in java, but it requires you to specify the parameter without providing a default value for you.
# Calculating a hashint N = 16384;int r = 8;int p = 1;String hashed = SCryptUtil.scrypt(passwd, N, r, p);# Validating a hashif (SCryptUtil.check(passwd, hashed)) { // Login successful}
0 × 03 C #. net c # (. NET) solution 1
We recommend Martin Steel's BCrypt. NET fork instead of System. Security. Cryptography. Rfc2898DeriveBytes, which is a PBKDF2-SHA1 (we don't mean that the PBKDF2-SHA1 is not secure, but bcrypt is relatively better.
// Calculating a hashstring hash = BCrypt.HashPassword(usersPassword, BCrypt.GenerateSalt());// Validating a hashif (BCrypt.Verify(usersPassword, hash)) { // Login successful}
C # (. NET) solution 2
Here there is also a Scrypt package in NuGET.
// This is necessary:ScryptEncoder encoder = new ScryptEncoder();// Calculating a hashSecureString hashedPassword = encoder.Encode(usersPassword);// Validating a hashif (encoder.Compare(usersPassword, hashedPassword)) { // Login successful}
0 × 04 Ruby first solution
As I have always done, here is a Ruby gem for the hash of bcrypt passwords.
require "bcrypt"# Calculating a hashmy_password = BCrypt::Password.create(usersPassword)# Validating a hashif my_password == usersPassword # Login successful
It should be noted that, as of the end of this article, the library still does not follow the best practices of encryption. Therefore, they need to take this into account before taking the time to patch.
Ruby solution 2
Scrpyt hash in Ruby
This is also a Ruby gem used to hash bcrypt passwords.
require "scrypt"# Calculating a hashpassword = SCrypt::Password.create(usersPassword)# Validating a hashif password == usersPassword # Login successful
0 × 05 first Python solution
All right, you guess. use the bcrypt python package (GitHub)
import bcryptimport hmac# Calculating a hashpassword = 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
Python developers usually prefer passlib (Bitbucket), although its API name is incorrect. ("Encryption" instead of "hash "):
from passlib.hash import bcrypt# Calculating a hashhash = bcrypt.encrypt(usersPassword, rounds=12)# Validating a hashif bcrypt.verify(usersPassword, hash): # Login successful
Python solution 2
Currently, we find that only django-scrypt package is available except libsodium. We are still searching for others.
0 × 06 Node. js Node. js solution 1
On Node. js, bcrypt (https://www.npmjs.com/package/bcrypt) has two secure implementations, Although bcrypt looks preferred.
In the following example, Promises is widely used to simplify error handling:
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});
Node. js solution 2
We recommend scrypt for humans, which is a node-scrypt package friendly to developers and is very easy to use.
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});
* Original article address: paragonie, Edison Chen of Dong'er men/compile 0xroot for later sorting. for details, refer to FreeBuf hacker and geek (FreeBuf. COM)