Сравнение хэша BCrypt между PHP и NodeJS

Для приложения, над которым я работаю, nodejs необходимо проверить хэши, созданные PHP и наоборот.

Проблема заключается в том, что хэши, сгенерированные в PHP (через класс Laravel Hash, который просто использует функцию PHP password_hash), возвращают false при тестировании в node.js.

Следующий node.js script:

var bcrypt = require('bcrypt');

var password = 'password';

var phpGeneratedHash  = '$2y$10$jOTwkwLVn6OeA/843CyIHu67ib4RixMa/N/pTJVhOjTddvrG8ge5.';
var nodeGeneratedHash = '$2a$10$ZiBH5JtTDtXqDajO6f4EbeBIXGwtcGg2MGwr90xTH9ki34SV6rZhO';

console.log(
  bcrypt.compareSync(password, phpGeneratedHash)  ? 'PHP passed' : 'PHP failed',
  bcrypt.compareSync(password, nodeGeneratedHash) ? 'nodejs passed' : 'nodejs failed'
);

выдает: "PHP failed nodejs прошел", тогда как следующий PHP script:

<?php

$password = 'password';

$phpGeneratedHash  = '$2y$10$jOTwkwLVn6OeA/843CyIHu67ib4RixMa/N/pTJVhOjTddvrG8ge5.';
$nodeGeneratedHash = '$2a$10$ZiBH5JtTDtXqDajO6f4EbeBIXGwtcGg2MGwr90xTH9ki34SV6rZhO';

print password_verify($password, $phpGeneratedHash)  ? 'PHP passed' : 'PHP failed';
print password_verify($password, $nodeGeneratedHash) ? 'nodejs passed' : 'nodejs failed';

выводит 'PHP передан nodejs прошло'.

Я запускал тесты в Ubuntu 14.04.1, используя PHP 5.5.18, node.js v0.10.32 и модуль npm bcrypt.

Ответ 1

Это не удается, потому что типы хэшей bcrypt, генерируемые из php и node, различны. Laravel генерирует $2y$, а node генерирует $2a$. Но хорошие новости - единственная разница между 2a и 2y - их префиксы.

Итак, что вы можете сделать, это сделать один из префиксов похожим на другой. Как:

$phpGeneratedHash  = '$2y$10$jOTwkwLVn6OeA/843CyIHu67ib4RixMa/N/pTJVhOjTddvrG8ge5.';
$nodeGeneratedHash = '$2a$10$ZiBH5JtTDtXqDajO6f4EbeBIXGwtcGg2MGwr90xTH9ki34SV6rZhO';

Что-то вроде:

$phpGeneratedHash  = '$2y$10$jOTwkwLVn6OeA/843CyIHu67ib4RixMa/N/pTJVhOjTddvrG8ge5.';
$nodeGeneratedHash = '$2y$10$ZiBH5JtTDtXqDajO6f4EbeBIXGwtcGg2MGwr90xTH9ki34SV6rZhO';

Обратите внимание, что я заменил $2a$ хеша node на $2y$. Вы можете просто сделать это с помощью

PHP

$finalNodeGeneratedHash = str_replace("$2a$", "$2y$", $nodeGeneratedHash);

Node

finalNodeGeneratedHash = nodeGeneratedHash.replace('$2a$', '$2y$');

Затем сравните phpGeneratedHash с finalNodeGeneratedHash.

Примечание. Если вы сравниваете PHP, измените префикс сгенерированного хэша NodeJS на $2y$ и, если вы сравниваете его в NodeJS; измените префикс хэширования, сгенерированного PHP, на $2a$.

Ответ 2

Я пытался вычислить то, что было сказано раньше, чтобы получить коды, которые работают. Как видите, мне не нужно ничего заменять.

На стороне PHP 7.2.4:

<?php
$password = "test123";
    $hash = password_hash($password, PASSWORD_BCRYPT);
    echo $hash; // I get $2y$10$5EaF4lMSCFWe7YqqxyBnR.QmDu1XhoiaQxrOFw.AJZkGCYmpsWDU6

На стороне узла JS:

Установить пакет bcryptjs: npm и bcryptjs

var bcrypt = require('bcryptjs');
let hash1="$2y$10$5EaF4lMSCFWe7YqqxyBnR.QmDu1XhoiaQxrOFw.AJZkGCYmpsWDU6";
console.log(bcrypt.compareSync("test123", hash1)); // display true

Ответ 3

Принятый ответ работает и с Yii2 Framework. Вот как я сюда попал

Ответ 4

Реализация bcrypt на разных языках может отличаться.

Например, в версии Node.js bcrypt.js длина соли составляет 29 символов

    bcrypt.getSalt = function(hash) {
        if (typeof hash !== 'string')
            throw Error("Illegal arguments: "+(typeof hash));
        if (hash.length !== 60)
            throw Error("Illegal hash length: "+hash.length+" != 60");
        return hash.substring(0, 29);
    };

Но в версии Go golang.org/x/crypto/bcrypt размер соли составляет 22 байта:

const (
    majorVersion       = '2'
    minorVersion       = 'a'
    maxSaltSize        = 16
    maxCryptedHashSize = 23
    encodedSaltSize    = 22
    encodedHashSize    = 31
    minHashSize        = 59
)

Так что может случиться так, что хешированная строка в Node.js получит ошибку при сравнении в Go, также как и в других языках.