암호화와 토큰을 이용한 API 인증 & 인가

Pien·2022년 9월 7일
0

위코드

목록 보기
7/10
post-thumbnail

사용자의 개인정보는 매우 민감하게 다뤄야 한다.
대부분의 사용자는 같은 비밀번호를 여러곳에서 쓰는 경우가 많은데, 한 사이트에서 비밀번호가 유출될 경우, 다른 사이트의 보안도 위험해 진다.
혹여나 DB가 유출될 경우를 대비해 비밀번호 같은 민감한 정보는 암호화가 필수다.
오늘은 Node.js에서 BcryptJWT 모듈을 사용해 DB에 암호화된 비밀번호 저장, 비밀번호가 일치 할 경우 Token을 발급해 신뢰성을 부여한다.

Bcrypt

오늘 사용할 Bcrypt모듈은 단방향 암호화를 지원한다.
단방향 암호화는 해시 알고리즘을 통해 해시값이 출력 되는데, 같은 값에 대해서는 동일한 해시값을 가진다.

Salting

평문에 단방향 암호화에 해시 알고리즘을 적용하면 해시값이 간단해진다.
이를 보완하기 위해 평문에 특정 문자를 추가 한뒤 해시 알고리즘을 실행하는데, 이를 Salting(솔팅) 이라고 한다.

Key Stretching

솔팅을 하더라도 해시 결과값은 그렇게 복잡하지 않다. 그래서 우리는 일정 횟수만큼 해쉬를 반복 실행해 해쉬값을 더욱 어렵게 만들어 DB가 노출되어도 민감한 정보가 노출되지 않게 한다.

Bcrypt 암호화

const bcrypt = require("bcrypt"); 

const password = '패스워드'; 
const saltRounds = 12;

const makeHash = async (password, saltRounds) => {
    return await bcrypt.hash(password, saltRounds);
}
 //예시 '$2b$12$LexMcb9ly1CZ0HfSr.hcL.qCFflGTD6o/7vxjHzEb6dxxvQAgxlae'

npm으로 bcrypt 모듈을 설치 하고 불러온다. 그 후 makeHash 함수를 선언해 bcrypt.hash 함수를 넣어준 뒤, 패스워드와 솔팅 횟수(해쉬 반복 수)를 넣어준다.
해당 함수 실행시 넣은 패스워드를 해쉬 알고리즘을 거친 뒤 암호문이 리턴 된다.

Bcrypt 검증

새로 입력한 패스워드와 DB에 저장된 암호화된 패스워드를 비교하고자 매번 새로운 패스워드를 암호화 한 뒤 비교를 하게 될 경우, 암호화에 소스가 많이 들기 때문에 자원낭비가 심하게 된다.
이를 위해 평문으로 입력된 패스워드와 해쉬 알고리즘을 거친 암호문을 같이 특정 함수에 넣을 경우 결과 값을 boolean 으로 반환해 준다.

const checkHash = async (password, hashedPassword) => {
    return await bcrypt.compare(password, hashedPassword) 
}
checkHash(패스워드, 암호화된 패스워드);

위의 checkHash 함수를 패스워드와 암호화된 패스워드를 같이 넣을 경우 맞으면 true 틀리면, false 값을 반환해준다.

JWT(JSON Web Token)

JWT는 사용자와 서버 사이에 정보를 JSON 개체로 안전하게 전송하기 위한 개방형 표준 입니다. 크기가 작아 데이터 전송에 무리가 없으며, 전자 서명이 되어있어 검증 과정을 거쳐 , 신뢰성을 가지고 있다.

JWT는 Header와 payload 그리고 signature 3가지로 이루어져 있으며, 구성 요소들은 dot(.) 으로 구분되어 있다.

JWT의 첫번째 구성 요소. 일반적으로 2가지의 정보를 담고 있다.

  • alg : Signature 을 만드는데 사용한 알고리즘 정보
  • typ : Token의 타입
{
  "alg" : "HS256",
  "typ" : "JWT"
}

위는 알고리즘 알고리즘 HS256사용, Token 타입 JWT라는 의미다.

Payload

JWT의 두번째 구성요소 실질적으로 전달해야 하는 정보들이 들어있다. Payload에 담긴 정보는 Claim이라고 부르며 3가지 종류가 존재한다.

  • Registered Claims : JWT 표준으로 지정된 Claim. 7가지가 존재하며 상황에 맞게 사용 한다.
    • iss : 토큰 발급자
    • sub : 토큰 제목
    • aud : 토큰 대상자
    • exp : 토큰 만료 시간
    • iat : 토큰 발급 시간
    • nbf : 토큰 활성화 시간
    • jti : JWT의 고유 식별자
  • Public Claims : JWT 사용자들에게 공개적인 데이터를 정의하는 장소.
  • Private Claims : 사용자와 서버사이에만 합의해 사용하는 장소
{
    "exp": "1245678900", //Registered Claims
    "https://velopert.com/jwt_claims/is_admin": true, //Public Claims
    "user_id" : 12345123 //Private Claims
}

Signature

JWT의 세번째 구성요소 Header와 Payload의 인코딩된 내용을 더하고 Secret Key와 알고리즘을 이용해 생성된 암호화된 값을 나타낸다.
암호화된 값을 Signature와 비교해 JWT의 신뢰성을 확인 할 수 있다. 서버에서 관리하는 Secret Key가 달라질 경우 Signature가 달라지기 때문에 새로운 토큰을 발급 받아야 한다.

JWT 발급

JWT 토큰을 발급받기 위해서는 jwt 모듈을 다운받아야 한다.

const jwt = require('jsonwebtoken'); 

const payLoad = { foo: 'bar' };
const secretKey = 'mySecretKey';
const jwtToken = jwt.sign(payLoad, secretKey);

console.log(jwtToken)
=> 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE2NTA1NTYxMzZ9.YAMgUMLhiVUwkRTr2rpOrIyWN0cTGLxsxZBqLAaKWUU'

payLoad 영역은 실제로 전달할 내용을 넣는다. 해당 영역은 공개 되기에 민감한 정보를 넣지 않는다.
secretKey는 코드상에서 노출되면 보안의 위험이 있기 때문에 환경변수로 주로 관리한다. 두개의 값을 넣고 `jwt.sign` 함수를 실행 시키면 3가지 dot(.) 으로 분류된 JWT 토큰이 발급 된다.

## JWT 검증
````js
const decoded = jwt.verify(jwtToken, secretKey); // (1)

console.log(decoded)
=> { foo: 'bar', iat: 1650555667 }

jwt.vertify 함수에 JWT토큰과 발급할때 사용한 secretKey를 인자로 넣어서 실행하면 일치하지 않을 경우 에러를 발생한다.
값으로 foo: 'bar'만 넣었는데 iat값도 같이 나와버렸는데, 이는 토큰 생성 시간을 표시한다.

마치며

개인정보 저장에 암호화는 필수다. 나의 안일함으로 암호화를 하지 않은 데이터가 유출 되었을 경우 사용자는 피해를 고스란히 받게 된다. 만에 하나 사고가 나더라도 데이터 유출을 막을 수 있는 데이터 암호화를 생활화 하자.

0개의 댓글