Token ( feat. JWT )
포스트
취소

Token ( feat. JWT )

해당 포스트는 Token, JWT, Access-Token, Refresh-Token의 개념과 이론적인 사용법 정리하는 포스트입니다.

인증 ( Authentication ): 로그인, 해당 사이트의 회원임을 인증 받는 것
인가 ( Authorization ): 인증받은 사용자가 서비스의 기능을 사용하는 인가를 받는 것

🪸 Hashing

암호화만 가능한 방식을 말합니다.
즉, 복호화가 불가능하기 때문에 암호화된 값이 노출되어도 조금은 더 안전할 수 있습니다.

대표적인 해시 함수 SHA1가 있고 주로 salt라는 방식을 이용해서 암호화합니다.
( salt를 사용하면 예측 가능한 암호들이 해킹당하는 것을 어느정도 막아줍니다. )

  • salt: 해시를 사용하는 방식은 그대로인데 해시 함수에 넣는 문자열에 특수한 문자열을 더해서 연산하기

  • Hashing의 특징

    1. 항상 같은 길이의 문자열을 리턴
    2. 동일한 해시 함수에 같은 문자열을 사용하면 항상 같은 값이 나옴

🪙 Token

인증에 사용하는 하나의 방식을 의미합니다.
( 다른 방식으로는 Session을 이용한 방식이 있습니다. )

0️⃣ Token vs Session

Token에 대해 알아보기 위해서는 Session과 비교해서 이해하면 도움이 됩니다.

Session은 서버의 메모리나 데이터베이스 즉, 서버의 자원을 이용해서 데이터를 관리합니다.
따라서 Session을 사용하면 서버의 자원을 어느정도 사용하게 됩니다.

이게 문제가 될 수 있는 게 서버는 하나 혹은 여러 개지만 클라이언트는 셀 수 없이 많습니다.
따라서 사용자가 늘어날수록 서버는 기존보다 더 많은 작업을 하지만 추가로 메모리의 부담도 발생하게 되기 때문에 문제가 될 수 있습니다.

이러한 Session의 한계를 극복하기 위해서 토큰 방식의 인증이 나왔습니다.

Session 보다 Token을 사용하는 것이 무조건 좋다는 의미가 아닙니다.
두 방식의 장단점에 대해 이후에 설명하겠습니다.

비교TokenSession
메모리브라우저에 저장서버에 저장
스케일링스케일링하기 편함스케일링 하기 불편함
관리관리 불가능관리 가능

스케일링이란 서버를 여러 대로 확장하는 것을 의미합니다.
만약 세션을 사용하는 서버를 스케일링한다면 나눠진 서버들이 모두 세션을 공유할 수 있도록 로직을 구현해야하게 됩니다.
반면 토큰의 경우에는 자체적으로 데이터를 갖기 때문에 토큰을 제작하는 데 사용한 비밀 키만 공유하면 됩니다.

관리측면에서는 세션은 서버에서 갖기 때문에 마음대로 수정/삭제를 할 수 있습니다.
하지만 토큰의 경우 브라우저가 갖기 때문에 한 번 내보내고 나면 서버에서 관리할 수 있는 방법이 없어집니다.

🍨 JsonWebToken

많은 Token들이 존재할 것이고 그 중에서 사용하는 서비스나 환경에 맞게 선택하면 됩니다.
하지만 저는 JsonWebToken만 사용해봤기 때문에 해당 토큰을 기준으로 작성하겠습니다.

JsonWebToken이란 JSON 객체에 정보를 담고 이를 Token으로 암호화하여 전송하는 기술입니다.
이후에는 어떻게 암호화하고 어떻게 검증하는지 알아보겠습니다.

0️⃣ 구조

크게 세 가지 구조로 나눠집니다.

0. Header

해당 Token을 설명하는 메타 데이터를 담는 부분입니다.

  • alg: 사용할 해시 알고리즘 ( HS256, RS256, ES256 등 )
  • typ: Token의 타입 ( JWT로 사용하면 됩니다. )
1
2
3
4
{
  "alg": "HS256",
  "typ": "JWT"
}

1. Payload

토큰에 넣고 싶은 데이터를 담는 부분입니다.
원하는 데이터를 담으면 되지만 너무 많이 담거나 노출되면 안 되는 정보를 담으면 안됩니다.
일반적으로 유저의 식별자나 DB에서 검색 자주하는 간단한 데이터를 담는 것 같습니다.

  • 약속된 값 ( 클레임 )
    1. iss: 토큰 발급자
    2. sub: 토큰 제목
    3. aud: 토큰 대상자
    4. exp: 토큰 만료 시간
    5. nbf: 작성한 시간 이후부터 유효한 토큰으로 처리
    6. iat: 토큰이 발급된 시간
    7. jti: 토큰 자체의 고유한 식별자
1
2
3
4
5
6
{
  "id": "1440",
  "admin": true,
  "iat": 1678274675716, // 발급 시간
  "exp": 1679484275716, // 만료 시간 ( 발급 시간으로부터 일주일 )
}

2. Signature

토큰이 유효한지 확인하는 데 사용하는 부분입니다.

SHA256 알고리즘을 사용하는 경우 Header + "." + Payload + Secret을 합쳐서 Signature를 생성합니다.

여기서 SecretJWT를 생성하고 검증할 때 사용할 고유의 문자열입니다.
어떤 값이라도 상관 없지만, secret, keyboardcat같은 흔한 문자열을 사용하면 다른 사람이 쉽게 알아낼 수 있기 때문에 고유한 값을 사용하면 좋습니다.

또한 생성할 때 Header, Payload를 사용하기 때문에 두 값을 변조한다면 검증을 통해 유효하지 않은 Token임을 알아낼 수 있습니다.
서버가 아닌 다른 곳에서 위조하기 위해서는 Secret이 필요하고 그 값이 노출되지 않는 것이 중요합니다.

1️⃣ Node에서 사용하기

Node.js에서 JWT를 생성하고 검증하는 방법에 대한 예시를 작성하겠습니다.

1. 설치

1
2
3
npm i jsonwebtoken

npm i -D @types/jsonwebtoken

2. 사용 예시

sign()으로 JWT를 생성하고 verify()JWT를 검증합니다.
생성한 JWT는 일반적으로 CookieAuthorization으로 브라우저로 전달합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { sign, verify } from "jsonwebtoken";

const payload = { name: "Aatrox", age: 26 };

const myJWT = sign(payload, "keyboardcat", {
  expiresIn: "1h",
});

console.log(myJWT);
/**
 * 가독성을 위해서 "." 기준으로 줄바꿈
 *
 * Header => eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
 * Payload => .eyJuYW1lIjoiQWF0cm94IiwiYWdlIjoyNiwiaWF0IjoxNjc4MjgyODkzLCJleHAiOjE2NzgyODY0OTN9
 * Signature => .y-TMGGg01ymOLheLa154EKn8ryoyJu3v4e_SiqZZN_A
 */

try {
  const verified1 = verify(myJWT, "keyboardcat");
  console.log("verified1 >> ", verified1);
} catch (error) {
  console.error("verified1 >> ", error);
}

try {
  const verified2 = verify(myJWT, "stangeValue");
  console.log("verified2 >> ", verified2);
} catch (error) {
  console.error("verified2 >> ", error);
}

/**
 * verified1 >>  { name: 'Aatrox', age: 26, iat: 1678283131, exp: 1678286731 }
 * verified2 >>  JsonWebTokenError: invalid signature
 */

🍻 AccessToken과 RefreshToken

여기서 코드에 대한 예시보다는 이론적으로 어떤 방식으로 동작하는지에 대해서 작성하겠습니다.
Token이라고 해서 특별한게 아니고 JWT로 사용할 수 있습니다.

AccessTokenRefreshToken을 사용하는 이유는 Token의 한계점을 어느정도 보완하기 위해서입니다.
TokenSession처럼 서버에서 관리하지 않기 때문에 한 번 생성하면 다시 회수하거나 제거할 수 없습니다.
따라서 한 번 탈취당하면 서버에서 대처를 할 수 없다는 의미입니다.
이런 위험성을 갖고 있기 때문에 인증을 처리하는 AccessToken은 유효기간을 짧게 하고, RefreshToken의 유효기간을 늘려서 주기적으로 AccessToken을 재발급하는 방식을 주로 사용합니다.

물론 이 방식도 완벽하지 않습니다.
RefreshToken 조차도 탈취당할 수 있기 때문입니다.
보안적으로는 항상 완벽은 없다고 들어서 완벽하지 않은 방법중에서 가장 현재 상황에 맞는 방법을 선택하는 것이 중요한 것 같습니다.

0️⃣ AccessToken

인가를 위해 사용하는 Token입니다.

1️⃣ RefreshToken

AccessToken의 유효기간이 지난 경우 재발급하는데 사용할 Token입니다.

2️⃣ 동작흐름

상황에 따라서 여러 방법이 있겠지만 Cookie를 통해서 AccessTokenRefreshToken을 브라우저에 전달한다고 가정하고 설명하겠습니다.

  1. 유저로 인증이 되었다면 해당 유저의 식별자를 Payload로 갖는 AccessToken, RefreshToken을 생성
  2. 생성한 Token들을 Cookie에 동봉해서 브라우저로 전달
  3. 브라우저에서 응답을 보고 자체적으로 쿠키를 등록하고 서버에게 요청할 때도 동봉해서 요청
  4. 서버에서는 쿠키를 열고 그 값인 Token을 유효한지 검사 후 인증된 유저인지 확인 후 특정 행동 실행
    • 만약 Token이 유효하지 않다면 401 응답
    • 만약 Token의 유효기간이 지났다면 RefreshToken 확인 후 문제 없다면 AccessToken 재발급

📮 레퍼런스

  1. inpa - JWT 토큰 인증 이란?
  2. Youbute - 포프 TV - JWT 토큰
  3. Youbute - 얄팍한 코딩사전 - 세션 VS. 토큰! JWT가 뭔가요?

  4. 1-blue - Cookie
  5. 1-blue - Session
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.