W jaki sposób node.bcrypt.js porównuje haszowane i zwykłe hasła bez soli?

96

Z github :

Aby zaszyfrować hasło:

var bcrypt = require('bcrypt');
bcrypt.genSalt(10, function(err, salt) {
    bcrypt.hash("B4c0/\/", salt, function(err, hash) {
        // Store hash in your password DB.
    });
});

Aby sprawdzić hasło:

// Load hash from your password DB.
bcrypt.compare("B4c0/\/", hash, function(err, res) {
    // res == true
});
bcrypt.compare("not_bacon", hash, function(err, res) {
    // res = false
});

Jak patrząc z góry, w porównaniach nie mogą występować wartości soli? Czego tu brakuje?

SChang
źródło

Odpowiedzi:

99

Sól jest włączana do skrótu (jako tekst jawny). Funkcja porównania po prostu wyciąga sól z hasha, a następnie używa jej do zaszyfrowania hasła i wykonania porównania.

Rachunek
źródło
1
Nadal nie rozumiem. Podczas porównania, skąd ma wiedzieć, która część haszyszu jest solą, jeśli nie dostarczysz jej z solą?
PoniedziałekPapier
7
bcrypt jest standardem i zawsze łączy sól z hashem w tym samym formacie. Dostarczasz sól podczas szyfrowania, a ta zostaje włączona do skrótu. bcrypt będzie w stanie odszyfrować tylko dane, które były pierwotnie zaszyfrowane za pomocą bcrypt, w przeciwnym razie masz rację - nie byłoby sposobu, aby dowiedzieć się, która część to hash, a która to sól.
Bill
6
OK, rozumiemy: sól jest przechowywana z haszyszem. bcrypt to oprogramowanie typu open source, co oznacza, że ​​każdy wie, jak dokładnie je przechowuje. Więc wiesz, jak go wyodrębnić lub jak wygenerować hash z hasła w postaci zwykłego tekstu. W jaki sposób pomaga to chronić hasła przed skanowaniem tęczowych tablic w poszukiwaniu skrótów, co jest w zasadzie główną ideą soli?
Vitaliy Lebedev
13
Nie ma znaczenia, czy napastnik zna sól dla danego haszyszu, to nie jest tajemnica. Użycie innej soli dla każdego hasła oznacza, że ​​osoba atakująca nie może wstępnie obliczyć skrótów przy użyciu wspólnych wartości. Mając inną sól na każdym z nich, musieliby ponownie obliczyć wszystkie tabele dla każdego hasła, co czyni je bezużytecznymi.
Bill
3
Spojrzenie w ten sposób, to ma znaczenie, jeśli atakujący zna sól dla określonego użytkownika w bazie danych, takich jak to: column_password = hash, column_salt = saltvs column_password = hash_salt. atakujący nadal ma te same informacje. Rzecz w tym, aby każde hasło było tak przypadkowe i większe, że jest mało prawdopodobne, aby ktoś je wstępnie obliczył.
Muhammad Umer
27

Miałem to samo pytanie, co na oryginalnym plakacie i trochę rozglądałem się i próbowałem różnych rzeczy, aby zrozumieć mechanizm. Jak już zauważyli inni, sól jest łączona z końcowym hashem. Oznacza to więc kilka rzeczy:

  1. Algorytm musi znać długość soli
  2. Musi również znać położenie soli w końcowej strunie. np. jeśli przesunięcie o określoną liczbę od lewej lub prawej strony.

Te dwie rzeczy są zwykle na stałe zakodowane w implementacji, np.źródło implementacji bcrypt dla bcryptjs definiuje długość soli na 16

/**
* @type {number}
* @const
* @private
*/

var BCRYPT_SALT_LEN = 16;

Aby więc zilustrować podstawową koncepcję stojącą za pomysłem, gdyby ktoś chciał zrobić to ręcznie, wyglądałoby to podobnie do poniższego. Nie polecam samodzielnego wdrażania takich rzeczy, gdy istnieją biblioteki, które możesz to zrobić.

var salt_length = 16;
var salt_offset = 0;

var genSalt = function(callback)
{
    var alphaNum = '0123456789abcdefghijklmnopqurstuvwxyzABCDEFGHIJKLMNOPQURSTUVWXYZ';
    var salt = '';
    for (var i = 0; i < salt_length; i++) {
        var j = Math.floor(Math.random() * alphaNum.length);
        salt += alphaNum[j];
    }
    callback(salt);
}

// cryptographic hash function of your choice e.g. shar2
// preferably included from an External Library (dont reinvent the wheel)
var shar2 = function(str) {
    // shar2 logic here 
    // return hashed string;
}

var hash = function(passwordText, callback)
{
    var passwordHash = null;
    genSalt(function(salt){
        passwordHash = salt + shar2(passwordText + salt);
    });

    callback(null, passwordHash);
}

var compare = function(passwordText, passwordHash, callback)
{
    var salt = passwordHash.substr(salt_offset, salt_length);
    validatedHash = salt + shar2(passwordText + salt);

    callback(passwordHash === validatedHash);   
}

// sample usage
var encryptPassword = function(user)
{
    // user is an object with fields like username, pass, email
    hash(user.pass, function(err, passwordHash){
        // use the hashed password here
        user.pass = passwordHash;
    });

    return user;
}

var checkPassword = function(passwordText, user)
{
    // user has been returned from database with a hashed password
    compare(passwordText, user.pass, function(result){
        // result will be true if the two are equal
        if (result){
            // succeeded
            console.log('Correct Password');
        }
        else {
            // failed
            console.log('Incorrect Password');
        }
    });
}
Alappin
źródło
1

Ponieważ sam miałem to samo pytanie, wiem dokładnie, o czym myślisz.

Masz błędne przekonanie między „Tajnym kluczem”, który jest używany w algorytmach kryptograficznych , a „solą”, która służy do spowolnienia procesu szyfrowania i utrudnia hakerom użycie brutalnej siły.

Kiedy używasz zwykłego hasła i soli do generowania skrótu, ten skrót używa samego hasła jako tajnego klucza ! Więc następnym razem, gdy spróbujesz porównać je ze zwykłym hasłem, musi być dokładnie tym samym hasłem, którego użyłeś do wygenerowania hasha! Dlatego nie musisz go przechowywać w innym miejscu, ponieważ jest on zawsze podawany przez użytkownika zarówno podczas rejestracji, jak i logowania!

babaliaris
źródło
1

Bcrypt porównuje zaszyfrowane i zwykłe hasła bez ciągu soli, ponieważ zaszyfrowane hasło zawiera ciąg soli, który utworzyliśmy w czasie mieszania.

Na przykład :

Weź to proste hasło:

546456546456546456456546111

Zaszyfrowane hasło powyższego zwykłego tekstu przy użyciu Bcrypt:

2 mld $ 10 $ uuIKmW3Pvme9tH8qOn / H7uZqlv9ENS7zlIbkMvCSDIv7aup3WNH9W

W powyższym zaszyfrowanym haśle znajdują się trzy pola rozdzielone symbolem $ .

i) Pierwsza część $ 2b $ określa używaną wersję algorytmu bcrypt.

ii) Druga część 10 $ 10 $ to czynnik kosztu (nic oprócz rund solnych podczas tworzenia sznurka soli. Jeśli zrobimy 15 rund, to wartość wyniesie 15 $

iii) Trzecia część to pierwsze 22 znaki (to nic innego jak łańcuch soli) W tym przypadku tak jest

uuIKmW3Pvme9tH8qOn / H7u

Pozostały ciąg to zaszyfrowane hasło. Zasadniczo więc saltedHash = salt string + hashedPassword do ochrony przed atakami tęczowej tablicy.

prashanth
źródło
1

To jest po prostu sznurek o stałej długości.

console.log("");
var salt = bcrypt.genSaltSync(10);
console.log(salt);
hash = bcrypt.hashSync("foobar", salt);
console.log(hash);

console.log("");
var salt = bcrypt.genSaltSync(10);
console.log(salt);
hash = bcrypt.hashSync("foobar", salt);
console.log(hash);

console.log("");
var salt = bcrypt.genSaltSync(10);
console.log(salt);
hash = bcrypt.hashSync("foobar", salt);
console.log(hash);
$2a$10$onmcKV.USxnoQAsQwBFB3e
$2a$10$onmcKV.USxnoQAsQwBFB3eytL3UZvZ5v/SudaWyaB9Vuq9buUqGO2

$2a$10$mwQfdyVS9dsO4SuxoR5Ime
$2a$10$mwQfdyVS9dsO4SuxoR5ImeG7atz7RXGRXb.c0VHp5zSn1N2VOA.Vq

$2a$10$uVUuJr6LryjchhKEg6PH7u
$2a$10$uVUuJr6LryjchhKEg6PH7unTw8aJGK0i3266c5kqDBLJkf80RHEpq

$2a$10$Y.upG5/54zvJyZacRxP17O
$2a$10$Y.upG5/54zvJyZacRxP17OH60BC0hQRMNfQjJxSWE77fyBrbzalmS
Jakub
źródło
0

Sól jest włączana do haszyszu. Funkcja porównania po prostu wyciąga sól z hasha, a następnie używa jej do zaszyfrowania hasła i wykonania porównania.

Kiedy użytkownik zaloguje się do naszego systemu, powinniśmy sprawdzić, czy wprowadzone hasło jest prawidłowe, czy nie. W przeciwieństwie do innych systemów, które odszyfrowałyby hasło w bazie danych (jeśli jest zaszyfrowane) i porównać je z wpisanym przez użytkownika, to, co robię z bcrypt (zakładając, że implementuje jednokierunkowe hashowanie), to zaszyfrowanie wpisanego przez użytkownik. Aby to zrobić, przekażę hasło do bcrypt, aby obliczyć hash, ale także hasło przechowywane w bazie danych skojarzonej z użytkownikiem (hash). Dzieje się tak, ponieważ, jak wspomniano wcześniej, algorytm bcrypt użył losowego segmentu (soli) do wygenerowania skrótu związanego z hasłem. Zostało to zapisane wraz z hasłem i potrzebujesz go do ponownego obliczenia skrótu hasła wprowadzonego przez użytkownika, a na koniec porównania z tym wprowadzonym podczas rejestracji i sprawdzenia, czy pasują.

Kumar Ajay AK
źródło