目前可接受的密码哈希算法主要有:

Argon2 密码哈希比赛的冠军

bcrypt

scrypt

以及密码哈希比赛的其他参赛算法 (Catena, Lyra2, Makwa, and yescrypt)

PBKDF2 最糟糕的一种选择
0×01 PHP

PHP第一种方案

首先确认你使用的版本是否支持PHP。如果支持,那么PHP密码API将能够使用。如果不支持,那么可以尝试进行升级,如果还是不行,检查一下password_compat。
$hash = password_hash($userPassword, PASSWORD_DEFAULT);

显然password_hash()使用的是加盐计算的哈希值。根据自己实际情况进行调整,如果硬件支持的话,使用最小绝对值10.12是很好的,其默认的10。
$hash = password_hash($userPassword, PASSWORD_DEFAULT, ['cost' => 12]);

事实上验证一个密码的哈希存储是很简单的:
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 } }

在PHP7中,PASSWORD_DEFAULT仍然使用bcrypt。在未来的版本中,它可能会逐渐变为Argon2.

PHP第二种方案

如果你没办法使用libsodium(当然我们强烈建议你使用),你仍然可以用PHP中加密哈希,只需要通过来自PECL的Dominic Black的Scrypt PHP扩展即可。

#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

接下来,把一个PHP包装捆绑在你的工程中。

# Hashing
$hash = \Password::hash($userProvidedPassword);
# Validation
if (\Password::check($userProvidedPassword, $hash)) {
    // Logged in successfully.
}
0×02 JAVA

JAVA第一种方案

在Java程序中进行安全的密码哈希,除了 libsodium还有jBCrypt,能够提供bcrypt密码哈希算法。
String hash = BCrypt.hashpw(userProvidedPassword, BCrypt.gensalt());
验证一个在Java中的bcrypt hash:
if (BCrypt.checkpw(userProvidedPassword, hash)) { // Login successful. }

JAVA第二种方案

有一个java实现的scrypt,但它需要你指定参数,而不会提供一个默认值给你。

# 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
}
0×03 C# .NET

C#(.NET)第一种方案

我们推荐Martin Steel的BCrypt.NET fork而不是System.Security.Cryptography.Rfc2898DeriveBytes,它是PBKDF2-SHA1(我们不是说PBKDF2-SHA1不安全,但相对来说bcrypt更好一些。

// Calculating a hash
string hash = BCrypt.HashPassword(usersPassword,    BCrypt.GenerateSalt());

// Validating a hash
if (BCrypt.Verify(usersPassword, hash)) {
    // Login successful
}

C#(.NET)第二种方案

这里同样有一个Scrypt 包 在NuGET中。

// 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
}
0×04 Ruby

Ruby 第一种方案

按照笔者一如既往的规律,这里是一个对bcrypt密码哈希的Ruby gem。

require "bcrypt"

# Calculating a hash
my_password = BCrypt::Password.create(usersPassword)
# Validating a hash
if my_password == usersPassword
  # Login successful

要注意的是截止本文发布,该库仍然没有遵循加密编码的最佳实践。因此在他们抽出时间打补丁前都需要考虑到这一点。

Ruby 第二种方案

在Ruby中进行Scrpyt哈希
这也是一个对bcrypt密码哈希的 Ruby gem。

require "scrypt"

# Calculating a hash
password = SCrypt::Password.create(usersPassword)
# Validating a hash
if password == usersPassword
  # Login successful
0×05 Python

Python 第一种方案

好了你猜的没错还是使用bcrypt python 包 (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

Python开发人员通常更喜欢passlib (Bitbucket),尽管它的API命名并不正确。(”加密” 而不是 “hash”):

from passlib.hash import bcrypt

# Calculating a hash
hash = bcrypt.encrypt(usersPassword, rounds=12)

# Validating a hash
if bcrypt.verify(usersPassword, hash):
    # Login successful

Python 第二种方案

目前我们发现除了libsodium以外只有django-scrypt package.可以较为健全的实现。其他的我们还在寻找中。

0×06 Node.js

Node.js 第一种方案

在Node.js上bcrypt(https://www.npmjs.com/package/bcrypt)有两种安全的实现方式,尽管bcrypt看起来是首选。

在下面的例子中,我们大量的使用了Promises来简化错误处理:

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 第二种方案

我们推荐 scrypt for humans,这是一个对开发人员很友好的 node-scrypt 包装器,非常容易使用。

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
});