node.bcrypt.js เปรียบเทียบรหัสผ่านที่แฮชและข้อความธรรมดาโดยไม่ใส่เกลือได้อย่างไร


96

จากgithub :

ในการแฮชรหัสผ่าน:

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

ตรวจสอบรหัสผ่าน:

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

จากข้างบนจะไม่มีค่าเกลือที่เกี่ยวข้องกับการเปรียบเทียบได้อย่างไร? ฉันพลาดอะไรไปที่นี่?

คำตอบ:


99

เกลือรวมอยู่ในแฮช (เป็นข้อความธรรมดา) ฟังก์ชันเปรียบเทียบจะดึงเกลือออกจากแฮชจากนั้นใช้เพื่อแฮชรหัสผ่านและทำการเปรียบเทียบ


1
ฉันยังไม่เข้าใจ ในระหว่างการเปรียบเทียบจะรู้ได้อย่างไรว่าส่วนใดของแฮชเป็นเกลือหากคุณไม่ได้ใส่เกลือ
จันทร์ที่

7
bcrypt เป็นมาตรฐานและเชื่อมเกลือกับแฮชในรูปแบบเดียวกันเสมอ คุณให้เกลือเมื่อคุณเข้ารหัสและสิ่งนี้จะรวมอยู่ในแฮช bcrypt จะสามารถถอดรหัสข้อมูลที่เข้ารหัสเดิมโดยใช้ bcrypt เท่านั้นมิฉะนั้นคุณพูดถูก - จะไม่มีทางรู้ว่าส่วนใดเป็นแฮชและส่วนใดเป็นเกลือ
บิล

6
ตกลงเราเข้าใจแล้ว: เกลือจะถูกเก็บไว้กับกัญชา bcrypt เป็นโอเพนซอร์สดังนั้นทุกคนจึงรู้ว่ามันจัดเก็บอย่างไร ดังนั้นคุณจึงรู้วิธีแยกมันหรือวิธีสร้างแฮชจากรหัสผ่านข้อความธรรมดา วิธีนี้ช่วยป้องกันรหัสผ่านจากการสแกนตารางสายรุ้งเพื่อหาแฮชซึ่งโดยพื้นฐานแล้วเป็นแนวคิดหลักที่อยู่เบื้องหลังเกลือ
Vitaliy Lebedev

13
ไม่สำคัญว่าผู้โจมตีจะรู้เกลือสำหรับแฮชใด ๆ ก็ตามไม่ใช่ความลับ การใช้เกลือที่แตกต่างกันสำหรับแต่ละรหัสผ่านหมายความว่าผู้โจมตีไม่สามารถคำนวณแฮชล่วงหน้าโดยใช้ค่าทั่วไปได้ ด้วยเกลือที่แตกต่างกันในแต่ละอันพวกเขาจะต้องคำนวณตารางใหม่สำหรับทุกรหัสผ่านซึ่งทำให้ไร้ประโยชน์
บิล

3
ดูวิธีนี้ไม่ได้ว่าถ้าโจมตีรู้เกลือสำหรับผู้ใช้บางอย่างในฐานข้อมูลเช่นนี้VScolumn_password = hash, column_salt = salt column_password = hash_saltผู้โจมตียังคงมีข้อมูลเดิม ประเด็นสำคัญคือการทำให้ทุกรหัสผ่านสุ่มและใหญ่ขึ้นจนไม่น่าจะมีคนคำนวณไว้ล่วงหน้า
Muhammad Umer

27

ฉันมีคำถามเหมือนกันกับโปสเตอร์ต้นฉบับและต้องมองไปรอบ ๆ และลองทำสิ่งต่างๆเพื่อทำความเข้าใจกลไก ตามที่ผู้อื่นชี้ให้เห็นแล้วเกลือจะถูกต่อเข้ากับแฮชสุดท้าย ดังนั้นนี่หมายถึงสองสิ่ง:

  1. อัลกอริทึมต้องทราบความยาวของเกลือ
  2. ต้องทราบตำแหน่งของเกลือในสตริงสุดท้ายด้วย เช่นถ้าหักล้างด้วยจำนวนเฉพาะจากซ้ายหรือขวา

สองสิ่งนี้มักจะถูกเข้ารหัสอย่างหนักในการใช้งานเช่นแหล่งที่มาของการใช้งานbcryptสำหรับbcryptjsกำหนดความยาวของเกลือเป็น 16

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

var BCRYPT_SALT_LEN = 16;

ดังนั้นเพื่อแสดงให้เห็นถึงแนวคิดพื้นฐานที่อยู่เบื้องหลังแนวคิดหากใครต้องการทำด้วยตนเองก็จะมีลักษณะคล้ายกับด้านล่าง ฉันไม่แนะนำให้ใช้สิ่งนี้ด้วยตัวเองเมื่อมีไลบรารีที่คุณสามารถทำได้

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

1

เพราะฉันมีคำถามเดียวกันกับตัวเองฉันรู้ว่าคุณกำลังคิดอะไรอยู่

คุณมีความเข้าใจผิดระหว่าง"Secret Key"ซึ่งใช้ในอัลกอริทึมการเข้ารหัสและ "Salt" ซึ่งใช้เพื่อชะลอกระบวนการเข้ารหัสและทำให้แฮกเกอร์ใช้กำลังดุร้ายได้ยากขึ้น

เมื่อคุณใช้รหัสผ่านธรรมดาและเกลือในการสร้างแฮชแฮชนี้จะใช้เป็นรหัสลับของรหัสผ่านนั่นเอง ! ดังนั้นในครั้งต่อไปที่คุณจะลองเปรียบเทียบกับรหัสผ่านธรรมดารหัสผ่านธรรมดานี้จะต้องเป็นรหัสเดียวกับที่คุณใช้สร้างแฮช! นี่คือเหตุผลที่คุณไม่ต้องเก็บไว้ที่อื่นเพราะผู้ใช้จะได้รับบริการทั้งในขั้นตอนการลงทะเบียนและเข้าสู่ระบบ!


1

Bcrypt เปรียบเทียบรหัสผ่านที่แฮชและข้อความธรรมดาโดยไม่มีสตริง Salt เนื่องจากรหัสผ่านที่แฮชมีสตริงเกลือที่เราสร้างขึ้นในขณะที่แฮช

ตัวอย่างเช่น :

ใช้รหัสผ่านธรรมดานี้:

546456546456546456456546111

แฮชรหัสผ่านของข้อความธรรมดาด้านบนโดยใช้ Bcrypt:

$ 2b $ 10 $ uuIKmW3Pvme9tH8qOn / H7uZqlv9ENS7zlIbkMvCSDIv7aup3WNH9W

ดังนั้นในรหัสผ่านที่แฮชด้านบนมีสามฟิลด์ที่คั่นด้วยสัญลักษณ์$

i) ส่วนแรก$ 2b $ระบุเวอร์ชันอัลกอริทึม bcrypt ที่ใช้

ii) ส่วนที่สอง$ 10 $ 10 เป็นปัจจัยต้นทุน (ไม่มีอะไรนอกจากรอบเกลือในขณะที่เราสร้างสตริงเกลือถ้าเราทำ 15 รอบมูลค่าจะเป็น$ 15 $

iii) ส่วนที่สามคือ22ตัวอักษรแรก(นั่นคืออะไรนอกจากสตริงเกลือ) ในกรณีนี้คือ

uuIKmW3Pvme9tH8qOn / H7u

สตริงที่เหลือคือรหัสผ่านที่แฮช โดยพื้นฐานแล้ว SaltHash = สตริงเกลือ + hashedPassword เพื่อป้องกันการโจมตีโต๊ะสายรุ้ง


1

มันเป็นเพียงสตริงที่มีความยาวคงที่

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

0

เกลือจะรวมอยู่ในกัญชา ฟังก์ชันเปรียบเทียบจะดึงเกลือออกจากแฮชจากนั้นใช้เพื่อแฮชรหัสผ่านและทำการเปรียบเทียบ

เมื่อผู้ใช้จะเข้าสู่ระบบของเราเราควรตรวจสอบรหัสผ่านที่ป้อนว่าถูกต้องหรือไม่ แตกต่างจากระบบอื่น ๆ ที่จะถอดรหัสรหัสผ่านในฐานข้อมูล (หากมีการเข้ารหัส) และเปรียบเทียบกับรหัสที่ป้อนโดยผู้ใช้สิ่งที่ฉันทำกับ bcrypt (เนื่องจากใช้การแฮชทางเดียว) จะเข้ารหัสที่ป้อนโดย ผู้ใช้ ในการทำเช่นนี้ฉันจะส่งรหัสผ่านไปที่ bcrypt เพื่อคำนวณแฮช แต่ยังรวมถึงรหัสผ่านที่เก็บไว้ในฐานข้อมูลที่เกี่ยวข้องกับผู้ใช้ (แฮช) เนื่องจากดังที่กล่าวไว้ก่อนหน้าอัลกอริทึม bcrypt ใช้เซ็กเมนต์สุ่ม (เกลือ) เพื่อสร้างแฮชที่เกี่ยวข้องกับพาสเวิร์ด สิ่งนี้ถูกเก็บไว้พร้อมกับรหัสผ่านและคุณจำเป็นต้องใช้เพื่อคำนวณแฮชของรหัสผ่านที่ผู้ใช้ป้อนใหม่และสุดท้ายเปรียบเทียบกับรหัสที่ป้อนเมื่อลงทะเบียนและดูว่าตรงกันหรือไม่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.