ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Express.js] 인증/인가(Bcrypt, JWT)
    Back-End/Express.js 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/

    반응형
Designed by Tistory.