[네트워크] 토큰

LMH·2023년 1월 7일
0

이번 포스팅에서는 토큰에 대한 개념에 대해 정리하겠습니다.

지난 포스팅에서 세션 기반 인증에 대해서 정리했습니다. 세션 기반은 데이터베이스에 세션에 대한 정보를 저장하고 사용자가 민감하거나 제한된 정보를 요청할 경우 그 정보를 제공해도 되는지에 대해서 세션 아이디를 통해서 검증합니다.

세션 기반 인증을 통해 더 안전하고 편리하게 사용자에게 정보를 제공할 수 있지만 요청마다 데이터베이스를 통한 검증이 필요하기 때문에 이는 서버에 부담을 줄 수 있습니다.

토큰 인증 방식을 사용하면 서버의 과부하를 줄이고 인증을 처리할 수 있습니다. 많은 토큰 인증 방식이 있지만 가장 대표적인 JWT(JSON web Token)을 다루도록 하겠습니다.

서버는 암호화된 토큰을 클라이언트에 발급하고 브라우저는 토큰을 저장한 후 토큰을 이용하여 인증 절차를 거칩니다. 토큰은 브라우저에 저장되는 만큼 철저한 암호화가 필요합니다.

JWT란?

JWT는 액세스 토큰과 리프레시 토큰을 이용하여 인증을 구현합니다.

액세스 토큰(Access Token)

엑시스 토큰은 보호되고 있는 정보들에 접근할 수 있는 권한부여에 사용합니다. 클라이언트가 처음 인증을 받게 될 때, 액세스 토큰과 리프레시 토큰을 받게 되며 정보에 접근할 때는 액세스 토큰을 사용합니다.

리프레시 토큰(Refresh Token)

그렇다면 리프레시 토큰은 왜 필요할 까요? 보안적인 측면에서 보자면 액세스 토큰이 외부에 유출될 경우 그 토큰을 이용하여 사용자인 척 악의적인 행동을 할 수 있습니다. 그렇기 때문에 대부분의 액세스 토큰은 유효기간을 짧게 설정해서 사용합니다.

리프레시 토큰은 액세스 토큰이 만료되면 새로 생성할 때 사용하는 토큰입니다. 액세스 토큰이 만료되고 리프레시 토큰을 사용해 새롭게 생성된 액세스 토큰을 브라우저에 저장하면 별도의 추가 인증을 거치지 않고 로그인 상태를 유지할 수 있습니다.

JWT 장점

서버가 클라이언트에 대한 정보를 저장하지 않아도 되며, 그렇기 때문에 하나의 토큰으로 여러 서버에서 인증이 가능하며, 어디서나 생성이 가능합니다. 그리고 암호화 키를 노출하지 않기 때문에 안전합니다.

JWT 단점

토큰을 사용한 인증은 서버의 과부하를 줄이고 사용자에게 편리함을 제공합니다. 하지만 리프레시 토큰이 유출될 경우, 사용자의 정보가 외부로 유출되는 등 큰 피해가 발생할 수 있습니다. 그렇기 떄문에 보안이 중요한 웹사이트의 경우에 리프레시 토큰을 사용하지 않아야 합니다. 물론 그렇게 된다면 사용자에게 편의성을 제공하긴 어려울 수 있지만 보안이 우선이 되어야합니다.

JWT 구조

JWT는 Header, Payload, Signature 세 부분으로 나누어져 있습니다.

Header는 토큰이 어떤 알고르즘으로 시그니처를 sign(암호화)할지에 대한 내용을 담고 있습니다. 아래의 JSON 형태의 정보를 base64 방식으로 인코딩한 값이 Header 부분에 들어갑니다.

{
 "alg" : "HS256", // HS256 알고리즘 사용
 "typ" : "JWT" 
}

Payload

Payload에는 서버에서 활용하는 유저의 정보가 담겨 있습니다. 유저의 이름과 같은 개인정보나 어떤 정보에 접근이 가능한지에 대한 접근 권한 등을 포함하고 있습니다. Header와 마찬가지로 base64로 인코딩 합니다.

{
 "sub" : "info"
 "name" : "lmh" 
 "iat" : 151623391 
}

Signature

base64로 인코딩된 header, payload를 바탕으로 Signature은 서버의 비밀 키(salt)를 이용하여 header에 명시한 알고리즘 방식으로 해싱을 합니다.
예시로, HMAC SHA256 알고리즘으로 생성한 Signature은 아래와 같은 방식으로 생성 됩니다.

HMACSHA256(base65UrlEncode(header) + '.' base65UrlEncode(payload), secret)

해싱

해싱은 암호화 방식 중 하나 입니다. 복호화가 가능한 다른 암호화 방식과는 달리, 암호화만 가능한 단방향 방식입니다. 해싱은 해시 함수를 사용하여 암호화를 진행합니다.

토큰 인증에서 해싱을 사용하는 이유는 사용자의 인증 정확한 인증정보를 알 필요 없이 서버에 있는 데이터와 동일한 값의 데이터인지 확인만 가능하면 되기 때문입니다.

해싱의 특징인 다음과 같습니다.

  • 항상 같은 길이의 문자열을 리턴합니다.
  • 서로 다른 문자열을 동일한 해시함수를 사용하면 반드시 다른 결과 같이 나옵니다.
  • 동일한 문자열에 동일한 해시 함수를 사용하면 같은 결과값을 가집니다.

비밀키(salt)

해싱할 경우 항상 같은 결과값이 나온다는 특성을 이용해 해시 함수를 거치기 이전의 값을 알아낼 수 있도록 기록해놓은 표인 레인보우 테이블이 존재합니다. 레인보우 테이블에 기록된 값의 경우에는 유출이 되었을 때 해싱을 했더라도 해싱 이전의 값을 알아낼 수 있으므로 보안상 위협이 될 수 있습니다.

그렇기 때문에 Salt라는 비밀 키를 더해서 데이터가 유출되어도 해싱 이전 값을 알아내기 어렵게 만듭니다.

<예시>
"password" + "salt" => ‘C88E9C67041A74E0357BEFDFF93F87DDE0904214’
"Password" + "salt" => ‘38A8FDE622C0CF723934BA7138A72BEACCFC69D4’

토큰 인증방식 구현

node.js에서 jsonwebtoen을 사용하면 토큰을 쉽게 생성할 수 있습니다.

토큰 생성 및 검증 함수

토큰을 생성하는 generateToken 함수는 user의 정보와 chekedkeepLogin(true or false) 값을 전달 받아 토큰을 생성합니다. 로그인을 유지하고자 하는 경우, 즉 chekedkeepLogin 값이 true인 경우 7일간 유효한 토큰을 생성하고 그렇지 않은 경우 1일간 유효한 토큰을 생성합니다.

토큰을 검증하는 verifyToken은 전달인자에 따라 aceess 또는 refresh 토큰의 비밀키를 이용해 verify 함수를 통해 디코딩 합니다.

토큰 생성 및 검증 시에는 환경변수로 저장되어있는 ACEESS_SECERT과 REFRESH_SECRET이라는 비밀키를 이용합니다.

require("dotenv").config();
const { sign, verify } = require("jsonwebtoken");

module.exports = {
  generateToken: async (user, checkedKeepLogin) => {
    const payload = {
      id: user.id,
      email: user.email,
    };
    let result = {
      accessToken: sign(payload, process.env.ACCESS_SECRET, {
        expiresIn: "1d", // 1일간 유효한 토큰을 발행합니다.
      }),
    };

    if (checkedKeepLogin) {
      result.refreshToken = sign(payload, process.env.REFRESH_SECRET, {
        expiresIn: "7d", // 일주일간 유효한 토큰을 발행합니다.
      });
    }
    return result;
  },
  verifyToken: async (type, token) => {
    let secretKey, decoded;
    switch (type) {
      case "access":
        secretKey = process.env.ACCESS_SECRET;
        break;
      case "refresh":
        secretKey = process.env.REFRESH_SECRET;
        break;
      default:
        return null;
    }

    try {
      decoded = await verify(token, secretKey);
    } catch (err) {
      console.log(`JWT Error: ${err.message}`);
      return null;
    }
    return decoded;
  },
};

느낀점

쿠키, 세션, 토큰 등 인증 방법에 대해서 알아보며 느낀점은 완벽한 인증 방법은 없다는 것 입니다. 인증 방법의 장단점에과 사용자 및 웹 사이트의 특성을 고려한 인증방법을 선택해서 사용해야 보다 편리하고 안전한 웹 사이트를 만들 수 있습니다.

profile
새로운 것을 기록하고 복습하는 공간입니다.

0개의 댓글