본문 바로가기
Node.jstesttset

[Node] - bcrypt

by 리슨업 2024. 1. 14.

 

 

 

[Node] -  bcrypt

#bcrypt

bcrypt는 Node.js의 암호화 라이브러리로서 전달 받은 본인만 알고있어야하는 문자열, 예로 로그인 비밀번호를

Hash 알고리즘을 통해 고정 길이의 해시값으로 변환하는 역할을 한다.

 

Hash값으로 변환 된  암호화 데이터는 이 시점 이후로 복호화가 불가능하게 된다.

해당 로직을 구현한 개발자도 알아 낼 수 없다.

 

다만 해커들은 레인보우테이블(rainbow Table) [각주:1]이나 무차별 대입 공격(brute-force attack) 등으로 비밀번호를 공격할 가능성이 있다.

 

 

#Salt

salt는 비밀번호를 hash값을 생성할 때 무작위로 원본데이터에 추가되는 문자열이다. salt의 길이는 해시 알고리즘에 따라 고정되어있고 알고리즘 내부에 따라 결정이 된다. 

이 salt로 인해 같은 비밀번호라도 다른 해시값을 가지게 되어 유일성을 보장 받게된다.

 

bcrypt에서는 saltRounds를 설정한다. saltRounds는 어려운 문자열을 쓰거나, 넓은 범위가 아닌 연산의 횟수를 지정한다. 해시 과정에서 더 많은 시간을 소요하게 함으로써 보안을 강화시키는 전략이다. 

 

 

" 그럼 무조건 복잡도를 높히는 것이 좋지않나? "

절대아니다.

saltRounds를 이용해 연산횟수를 늘리면 물론 보안은 강화되겠지만, 복잡도가 증가하면 해시 연산을 하는데 걸리는 시간은 기하급수적으로 늘어나게 되고, 리소스를 과다하게 소비하게되어 서버 컴퓨터의 Cpu에도 자원낭비가 된다.

 

이 이야기는 사용자가 이용할 때도 연산작업을 하기 때문에 매번 해당 서비스 이용할때 지연되는 상황을 초래하게 된다.

보안을 강하하려다 사용자에게 불편함을 주게 되는 꼴이 될 수도 있는 것이다. 

 

이로써 적절한 벨런스의 saltRounds 값을 설정해야 한다. 평균적으로 8 ~ 10 정도의 saltRounds 값은 리소스 부담을 크게 늘리지 않는다.

 

 

#bcrypt 메서드

1) hash

bcrypt.hash(data, saltRounds, callback)


비밀번호나 다른 데이터를 해시화하는 메서드, saltRounds는 해시를 생성할 때 사용되는 솔트의 복잡도를 결정하고 callback 함수는 해싱이 완료되었을 때 호출된다. 

hash는 비동기 메서드로 주로 함수로 감싸 프로미스를 선언하는것이 일반적이다.

 


2) hashSync:

 bcrypt.hashSync(data, saltRounds)


hash 메서드의 동기 버전, 주어진 데이터를 해시하고, 그 결과를 바로 반환한다.

 

 

3) compare

bcrypt.compare(data, hashed, callback)


사용자가 입력한 비밀번호와 해시된 데이터를 비교한다.

일치하면 callback 함수에 true를, 그렇지 않으면 false를 리턴한다.

 


4) compareSync

bcrypt.compareSync(data, hashed)


compare 메서드의 동기 버전, 데이터가 해시된 데이터와 일치하는지 여부를 바로 반환

 


5) genSalt

bcrypt.genSalt(saltRounds, callback)


솔트(salt)를 생성한다. saltRounds는 생성될 솔트의 복잡도를 결정하고 callback함수로 err, salt를 매개변수로 받는다.

 


6) genSaltSync

bcrypt.genSaltSync(saltRounds)


genSalt 메서드의 동기 버전, 솔트를 바로 생성하고 반환

 

 

 

#bcrypt 사용예시

 

1) 해시값 생성

const { 
    genSalt , 
    hash 
} = require('bcrypt');

const password = 'password'; //비밀번호
const saltRounds = 10;

const addHash = async() =>{
    try{
        const hashData = await hash(password , saltRounds);
        console.log(hashData);
    }
    catch(err){
        throw new Error('해시생성 실패');
    }
}
addHash();

 

디스트럭처링으로 genSalt, hash 매서드를 이용하여 만든 로직이다. addHash 함수를 호출하고 hash의 경우 비동기 동작을 하기에 프로미스를 반환하도록 하여야 <pending> 을 막을 수 있을것이다. 

 

nodemon으로 매번 새로고침해도 해시값은 매번 변경되는 것을 볼 수 있다. 

 

 

 

2) Compare를 이용한 암호 확인

const { 
    genSalt , 
    hash ,
    compare
} = require('bcrypt');

const password = 'password'; //비밀번호
const hashPassword = '$2b$10$tfZYlxfgSEEkEvXsh7yoUOoWZh3QoTqa18WD6IRpXXzaU0OaLbRbi';

const comparePassword = (password , hashPassword) =>{
    return compare(password , hashPassword);
}

const main = async() =>{
    const data = await comparePassword(password , hashPassword);
    console.log(data);
}

main();

 

hashPassword는 이전에 만들어 두었던 해싱된 패스워드이다. 해당 패스워드는 DataBase에서 다른 로직을 통해서 가져온다 가정하고 compare 부분에서 password와 hashPassword를 비교하면 "true""false" 값을 리턴하게 된다. 

 

이로써 로그인 성공여부를 결정하고 토큰을 발급해주면 기본적인 로그인 인증 로직이 된다. 

compare 메서드 또한 비동기로 동작함으로 comparePasswordawait 해주면 불리언 값을 를 받아올 수 있게 된다.

 

 

 

 

  1. 레인보우테이블 : 해시 함수를 사용하여 변환 가능한 모든 해시 값을 저장시켜 놓은 표 [본문으로]

'Node.jstesttset' 카테고리의 다른 글

[Node] JWT , Login 인증  (0) 2024.01.16
Node.js 미들웨어란?  (0) 2023.11.27
Node.js 동기/ 비동기 에러처리  (1) 2023.11.23