2016 年存储密码的最佳算法

Posted

技术标签:

【中文标题】2016 年存储密码的最佳算法【英文标题】:best algorithm to store passwords in 2016 【发布时间】:2016-09-24 01:34:03 【问题描述】:

其实我看了很多关于算法的文章,比如md5sha1等等。但是我仍然不确定哪一个是当今最安全和最好的。我是 Web 开发的初学者,我要求世界上所有最好的程序员来教我。我希望你们能给我选择和使用它的例子。谢谢你

【问题讨论】:

现在最好用的是password_hash()和password_verify(),php文档页面中有例子;更不用说PHP The Right Way @MarkBaker password_hash() 不是算法,而是使用 bcrypt/crypt_blowfish 算法的函数。 @Daan - 只是想避免一次性将太多信息混淆给初学者 感谢您的回答。我会更多地研究它 @MalayMuslim - 如果你对安全密码存储的基础知识感兴趣,你可以看看我的tutorial。 【参考方案1】:

顺便说一句:How to safely store your users' passwords in 2016.

您的选择是:

Argon2(需要 PHP 7.2 或 PHP 扩展) Scrypt(需要 PHP 扩展) Bcrypt

如果你真的需要,也可以考虑使用 PBKDF2。

旧备用:Bcrypt

鉴于您是初学者,您应该这样编写密码验证:

// Creating your hashed password:
$hash = password_hash($userPassword, PASSWORD_DEFAULT);

// Checking a user-supplied password against a stored hash:
if (password_verify($userPassword, $hash)) 
    // Login successful.
    if (password_needs_rehash($hash, PASSWORD_DEFAULT)) 
        // Recalculate a new password_hash() and overwrite the one we stored previously
    

bcrypt 的缺点:

超过 72 个字符的密码将被截断。 带有 NUL 字节的密码将被截断。

Password Lock 内置了一个解决这些限制的权宜之计:它使用 SHA384 对密码进行预散列,然后在传递给 PHP 的密码 API 之前对原始散列进行 base64 编码。

首先,创建一个加密密钥并将其存储在文档根目录之外。 (否则,黑客只能窃取密钥。)

$newKey = \Defuse\Crypto\Key::createNewRandomKey();
file_put_contents(
    '/outside/document/root/enckey.txt',
    $newKey->saveToAsciiSafeString()
);

现在,您可以将此密钥与您的密码结合使用:

$key = Key::loadFromAsciiSafeString(
    file_get_contents('/outside/document/root/enckey.txt')
);

// Hashing a password with PasswordLock:
$storeMe = PasswordLock::hashAndEncrypt($_POST['password'], $key);

// Verifying a password with PasswordLock:
if (PasswordLock::decryptAndVerify($_POST['password'], $storeMe, $key)) 
    // Success!

You can now use Argon2 with password_hash() in PHP 7.2

新标准:Argon2(通过 Libsodium)

除非您使用的是 PHP 7.2 或更高版本,否则您需要 install libsodium and the PHP extension 才能使用 Argon2。密码哈希是 sodium_compat提供的功能之一。

// 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.

中级:Scrypt

您需要the scrypt extension,可通过 PECL 获得:

pecl install scrypt
echo "extension=scrypt.so" > /etc/php5/mods-available/scrypt.ini
php5enmod scrypt

安装后,使用起来相当简单:

// Hashing:
$hash = \Password::hash($userProvidedPassword);
// Validation:
if (\Password::check($userProvidedPassword, $hash)) 
    // Logged in successfully.

真正使用 scrypt 的唯一原因是兼容性;此时,使用 Argon2 或 bcrypt。

可接受但不是很好:PBKDF2

如果您需要 PBKDF2,我强烈建议您使用 Defuse Security's cross-platform Password Hashing library。 (不过,您应该考虑只使用password_*!)

$hash = PasswordStorage::create_hash($password);
if (PasswordStorage::verify_password($password, $hash)) 
    // Success


以上任何选择都是可接受的。 Argon2 可能是最安全的,但它还没有在 PHP 中广泛使用。此列表中缺少的任何内容都应被视为健康的怀疑态度。

【讨论】:

既然您没有提到 phpass (openwall.com/phpass),您如何评价它在 2016 年的哈希选项中? PHPPass 是 bcrypt,除非您使用的是 WordPress。除非必须,否则不要使用它;请改用password_*【参考方案2】:

重要的是,该算法提供了一个成本因素,它控制计算哈希所需的时间。您在计算单个哈希上投入的时间越多,暴力破解的代价就越大(例如,100 Giga MD5 per second 与每秒 10 个 BCrypt)。

今天推荐的算法是 BCrypt、PBKDF2 和 SCrypt。 PHP 支持 BCrypt 算法,一个包装函数负责生成盐,并且是面向未来的。

// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
$hashToStoreInDb = password_hash($password, PASSWORD_DEFAULT);

// Check if the hash of the entered login password, matches the stored hash.
// The salt and the cost factor will be extracted from $existingHashFromDb.
$isPasswordCorrect = password_verify($password, $existingHashFromDb);

【讨论】:

以上是关于2016 年存储密码的最佳算法的主要内容,如果未能解决你的问题,请参考以下文章

SM3加密与解密校验

mysql 最新的密码哈希算法/数据加密是啥?

页面置换算法(最佳置换算法FIFO置换算法LRU置换算法LFU置换算法)

PHP加密算法

21 | 哈希算法(上):如何防止数据库中的用户信息被脱库?

加密密码php的最佳方式(2017年)[重复]