자료구조 Hash에서 정리했듯이 crypto를 활용하여 비밀번호를 암호화 하는 방식에 대해서  왜 사용을 하였는지에 초점을 두고 정리를 하고자합니다.

솔트(Salt)란?

암호학에서 솔트는 데이터, 비밀번호, 통과암호를 해시 처리하는 단방향 함수의 추가 입력으로 사용되는 랜덤 데이터입니다.

솔트(Salt)는 왜 사용한 것인가요?

예를 들어, 'jeong-park' 이라는 스트링을 sha512로 해시 해보겠습니다.
"2C1E444BE95BCB0236B7C82FDA667EC441B7A7E087898EA3299512675FADB1D65106905CEF0F757CB0D96A1AB82F97B83D3C64A8F30052893E6A8EC46D5935AA" 
위처럼 나옵니다. 제가 아닌 다른 사람들이 'jeong-park'이라는 스트링을 입력하여 sha512로 해시하면 똑같이 나옵니다.

항상 똑같은 결과값을 반환한다면?
해시 함수를 사용하여 변환 가능한 모든 해시 값을 저장시켜 놓고 저장시켜 놓으면 원래의 비밀번호를 추출(레인보우 테이블)해낼 수 있지 않을까요?
이러한 문제 때문에 Salt을 사용하며, 또한 Key Stretching 이라는 친구를 사용합니다.
 
* Key Stretching은 해시된 값을 또 다시 해시 함수의 입력 값으로 넣어서 해싱을 반복하는 것입니다. 이런 방식으로 무작위 대입을 통해 해시 값을 구하는 시간을 늘려서 레인보우 테이블이나 무차별 대입 공격을 어렵게 하기 위함이라고 합니다.

1. createSalt : randomBytes 메소드로 64바이트 길이의 salt를 생성합니다. new_salt는 버퍼형식으로 나오기 때문에 toString("base64")을 사용하여 리턴해줍니다.
2. createHashedPassword : pbkdf2(유저가 입력한 비밀번호, 만들어준 솔트값, 키스트래칭, 비밀번호의 길이, 해시함수) 입니다. 

(유저가 입력한 비밀번호 + 솔트 값) => Hash 값을 * Key Stretching 만큼 반복 하여 = HashedPassword를 만들어 줍니다.

const crypto = require('crypto');
const util = require('util');

const pbkdf2Promise = util.promisify(crypto.pbkdf2);
const randomBytesPromise = util.promisify(crypto.randomBytes);

module.exports = {

    /**
     * Salt Create 
     * @returns String
     */
    createSalt: async () => {
        const new_salt = await randomBytesPromise(64);
        return new_salt.toString("base64");
    },

    /**
     * Create HashedPassword
     * @param {String} password 
     * @returns String, String
     */
    createHashedPassword: async (password, currnet_salt) => {
        const salt = currnet_salt;
        const key = await pbkdf2Promise(password, salt, Number(process.env.KEY_STRETCHING), 64, "sha512");
        const hashedPassword = key.toString("base64");
        return { hashedPassword, salt };
    },

    /**
    * verifyPassword
    * @param {String} password 
    * @param {String} userSalt 
    * @param {String} userPassword 
    * @returns boolean
    */
    verifyPassword: async (password, userSalt, userPassword) => {
        const key = await pbkdf2Promise(password, userSalt, Number(process.env.KEY_STRETCHING), 64, 'sha512');
        const hashedPassword = key.toString('base64');
        if (hashedPassword === userPassword) return true;
        return false;
    }
}

 

참고 사이트 : https://ko.wikipedia.org/wiki/%EC%86%94%ED%8A%B8_(%EC%95%94%ED%98%B8%ED%95%99)

+ Recent posts