Back-End/Express.js

[Express.js] 인증/인가(Bcrypt, JWT)

ttaeng_99 2021. 2. 4. 06:14
반응형

🤯 서론

요즘 Express 공부에 대부분의 시간이 할애되는 것 같다... (공부를 가장한 구글링의 늪 👾👾)

오늘은, 로그인/회원가입 기능을 만들면서 적용한 비밀번호 암호화(Bcrypt)와 토큰발행(JWT)에 대해 간단히 포스팅하고자 한다.


📗 Bcrypt

1. Bcrypt 란?

1999년에 Niels Provos와 David Mazieres가 발표한 가장 강력한 단방향 비밀번호 해시 메커니즘 중 하나이다. C, C++, C#, Go, Java, PHP, Perl, Python, Ruby등의 언어를 지원한다. (해싱(hashing)은 임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 것을 의미)

  * 암호화의 종류에는, SHA, PBKDF2, Scrypt 등이 존재한다. 각각은 속도와 보완성에 따른 장단점이 존재한다.

Bcrypt는 단방향 해시 함수라는 특징을 가지며, 이는 곧 암호화된 메세지(digest)를 생성하더라도, 다시 평문으로 볼 수 없다는 강점이 있다.

 

즉, Bcrypt 모듈(Node.js)을 통해, 회원가입 시 받아온 비밀번호 정보를 암호화하여 DB에 저장할 수 있는 것이다.

 

 

2. Bcrypt의 문제점과 보완책

- 문제점

1) 동일한 비밀번호는 동일한 digest(해시값)을 생성한다. 비밀번호 별 해시값 테이블이나 해시패턴을 역추적하면 복호가 가능하다.

2) 해시 함수의 빠른 처리속도가 역으로 단점이 된다. 대량의 digest와 대조하면서 비밀번호 원본을 찾아낼 수 있는 것이다.

 

- 보완책

 

1) 솔팅(salting)

말 그대로 소금을 친다고 생각하자! 해시값 사이사이에 임의 문자열을 추가하여 digest를 생성하는 방법이다.

 

2) 키 스트레칭(key stretching)

입력한 비밀번호의 digest를 생성하면, 생성된 digest를 다시 해싱하여 해시값을 연장하는 것이다. 복호를 그만큼 지연시킬 수 있다.

 

3. Bcrypt 모듈 종류

Node.js에서는 bcryptbcryptjs 두 가지 모듈을 제공한다. 차이점은, bcrypt는 C++, bcryptjs는 JS 기반이라는 것이다.

둘 중 아무 모듈이나 상관없지만, 나는 벤치마크 테스트에서 bcrypt가 더 빠르다는 글을 보고 단순하게 bcrypt를 적용했다.

(Node.js의 Bcrypt와 BcryptJS 벤치 마크 : ichi.pro/ko/post/116178465148098)

 

 

4. Bcrypt 모듈 설치 및 사용

- 설치 

npm install bcrypt

 

 

- bcrypt.hash (회원가입)

// bcrypt 선언
const bcrypt = require('bcrypt');

// 비밀번호 해싱
const { password } = req.body;
const hashedPassword = await bcrypt.hash(body.password, 10);

// 유저DB 추가
usersDB.push({
  id: Date.now().toString(),
  ...body,
  password: hashedPassword,
})

bcrypt를 가져온 뒤, hash() 메서드를 통해서 비밀번호 값을 암호화할 수 있다.

첫 번째 인자로 값, 두 번째 인자로 salt round 수, 세 번째 인자로는 암호화된 비밀번호의 처리함수(DB저장 등)를 콜백으로 받는다.

 

또한, hashedPassword 에서 확인할 수 있듯이, bcrypt 암호화는 비동기로 처리해야 함을 알 수 있다.

반드시, 해싱이 진행되는 함수를 async로, bcrypt.hash() 메서드로 해싱하는 부분을 await 를 적용해야한다. (미적용시 undefined 반환)

  * 공식문서를 보니, hashSync() 메서드를 활용하면 비동기로 해싱 처리가 가능한 것 같다!

 

위처럼, 내가 입력한 비밀번호가 암호화(해싱)되어 저장된 것을 알 수 있다.

 

 

- bcrypt.compareSync (로그인)

 

관리자들도 알 수 없도록 비밀번호는 암호화되지만, 로그인 시 비밀번호 일치여부를 확인할 수 있어야 한다.

이를 위한 메서드가 compare() 이다. (마찬가지로, compareSync()비동기로 대조 처리를 하는 메서드이다.)

const userCompared = bcrypt.compareSync(password, userExisted.password);

위처럼, 메서드 인자는 첫 번째로 입력된 값(non-hashed), 두 번째로 비교될 값(hashed) 순으로 입력한다. (boolean을 반환)

 

아래는, compare() 을 사용한 예시이다.

// compare(data, encrypted, callback)
bcrypt.compare(myPlaintextPassword, hash, function(err, result) {
    // result == true
});

bcrypt.compare(someOtherPlaintextPassword, hash, function(err, result) {
    // result == false
});


// compare(data, encrypted).then(asynchronous)
bcrypt.compare(myPlaintextPassword, hash).then(function(result) {
    // result == true
});

 

.then() 혹은 hashSync(), compareSync() 등 비동기 해싱을 하는 경우는, 회원정보를 DB에 입력/불러오기 하는 경우에 선호된다.


📗 JWT(Json Web Token)

1. JWT(Json Web Token) 이란?

사용자 정보를 JSON 객체에 담아, 이를 암호화하고 해싱 작업을 거쳐 문자열 토큰으로 생성하는 기술이다.

출처 : https://research.securitum.com/jwt-json-web-token-security/

JWT는 위처럼, Header, Payload, Signature 세 영역으로 나뉘어져 있으며, 각각의 정보들이 해싱되어 위처럼 문자열 토큰이 된다.

  • Header: 해싱 알고리즘(SHA-2 = HS256)토큰 타입(JWT)이 명시되어 있다. 토큰 해독을 위해 알고리즘을 참고한다.
  • Payload: 토큰에 담을 정보(Claim)들이 들어있다. iat(Issued At Time)같은 표준 클레임과, 사용자 지정 클레임 등이 포함된다.
  • Signature: 토큰을 인코딩하는 BASE64-URL과, header/payload를 '.'으로 구분, JWT에 사용된 secret key 등이 포함된다.

 

2. jsonwebtoken 설치 및 발급

- 설치

npm install jsonwebtoken

Node.js 에서 제공하는 'jsonwebtoken' 모듈을 설치 및 활용하면 된다.

 

- 발급

const jwt = require('jsonwebtoken');

const token = jwt.sign(req.body, 'MY_SECRET_KEY');
response = {
  success: true,
  message: 'SUCCESS',
  token: token,
}
  1. jwt 변수에 'jsonwebtoken' 을 불러온다.
  2. sign() 메서드를 활용한다. 첫 번째 인자는 담을 정보(payload), 두 번째 인자는 Secret Key를 받는다.
  3. 세 번째 인자는 알고리즘으로, 미설정시 default인 SHA256 알고리즘이 적용된다.

* 다른 알고리즘(RS256) 을 적용하고자 하는 경우

// sign with RSA SHA256
var privateKey = fs.readFileSync('private.key');
var token = jwt.sign({ foo: 'bar' }, privateKey, { algorithm: 'RS256'});

 

3. JWT의 장단점

- 장점

  • 주요정보가 토큰의 payload에 저장되어, 별도의 저장소가 필요없다. 서버는 인증만 처리하면 된다.
  • 토큰은 개인 비밀키에 따라 관리되므로, 위변조의 영향을 적게 받는다. 다만, Claim에는 노출되어도 무방한 정보만을 권장한다. 

- 단점

  • 두 번째 장점과 연관된 부분으로, Claim이 노출될 가능성이 있어 중요한 정보는 payload에 담을 수 없다.
  • Claim 크기에 따라 토큰의 길이가 길어지므로, 정보량이 많아질수록 토큰의 용량과 트래픽 속도에 영향을 미친다.
  • JWT 토큰은 유효기간이 만료될 때 까지 사용이 가능하므로, 기한내 탈취되어 악용될 가능성이 있다.

 

토큰은 비밀키나, 유효성 검사(관리자, 사용자 구분?) 등 다뤄야할 부분이 매우 많다.

추후, admin 페이지 제작이나, 장바구니 페이지 구현(회원만 사용가능)을 할 경우, 토큰인증 부분을 공부해서 추가 포스팅을 해야겠다.


위코드 인증/인가 세션에서 비밀번호 암호화나 회원 토큰발행을 막연하게 들었고, 사실 프로젝트도 백엔드에서 위임을 했었다.

 

이를 직접 공부해보고, 내 서버의 로그인/회원가입 로직에서 암호화된 비밀번호도 저장해보고 로그인된 회원에 대한 response에 토큰을 첨부해보니 막상 재밌기도 하면서 뿌듯했다.😃😃

단촐하지만, Full-Stack의 꿈에 한발짝 다가간 것 같아 설레면서도, 백엔드 역시 만만치 않은 영역임을 조금이나마 느꼈다.!

 

내일은, 동적 라우팅과 페이지네이션을 위한 라우팅과 URL 파라미터에 대해 포스팅하겠다.

 

[출처]

- npm 공식문서(bcrypt) : www.npmjs.com/package/bcrypt  

- sungjun-jin 님의 블로그 : velog.io/@sungjun-jin/bcrypt  

- npm 공식문서(jsonwebtoken) : www.npmjs.com/package/jsonwebtoken?activeTab=readme

- ING-YEO 님의 블로그 : ing-yeo.net/2020/02/study-nodejs-jwt-authorization/

반응형