JWT 암호화

박병현·2024년 6월 26일

JWT 암호화 방식 비교: 비대칭 암호화 (RS256) vs 대칭 암호화 (HS256)

1. 개요

JWT(JSON Web Token)는 인증 및 정보 교환을 위한 JSON 기반의 웹 토큰입니다. JWT는 두 가지 주요 암호화 방식을 사용하여 서명될 수 있습니다: 비대칭 암호화(RS256)과 대칭 암호화(HS256). 이 문서에서는 두 가지 암호화 방식의 차이점을 비교하고, 각각의 사용 예시를 제공합니다.

2. JWT 구성 요소

JWT는 세 부분으로 구성됩니다: Header, Payload, Signature(서명).

헤더는 토큰의 유형과 서명에 사용된 알고리즘 정보를 포함합니다. 일반적으로 다음과 같이 구성됩니다:

{
  "alg": "RS256",
  "typ": "JWT"
}

Payload

페이로드는 토큰의 클레임을 포함합니다. 클레임은 토큰에 담을 정보로, 사용자 정보나 토큰의 만료 시간 등이 포함될 수 있습니다. 페이로드의 정보는 JSON 형식으로 구성됩니다.

페이로드에 담기는 정보는 세 가지 유형으로 나뉩니다:

  1. 등록된 클레임 (Registered Claims): 토큰에 대한 정보를 담기 위해 이미 정해진 이름의 클레임입니다. 예를 들어, 토큰 발급자 iss, 토큰 만료 시간 exp, 토큰 발급 시간 iat 등이 있습니다.
  2. 공개 클레임 (Public Claims): 충돌이 방지된 이름을 가지고 있어야 합니다. 클레임 이름을 URI 형식으로 짓는 것이 일반적입니다. 예를 들어, {"https://example.com/is_admin": true}와 같은 형식입니다.
  3. 비공개 클레임 (Private Claims): 클라이언트와 서버 간에 협의하여 사용되는 클레임 이름들입니다. 예를 들어, {"username": "user1"}와 같은 형식입니다.

페이로드 예시:

{
    "iss": "example.com",
    "exp": 1485270000000,
    "https://example.com/is_admin": true,
    "userId": "user1",
    "username": "alex"
}

Signature

서명은 헤더와 페이로드를 인코딩한 후, 이를 비밀 키(대칭 암호화) 또는 개인 키(비대칭 암호화)로 해싱하여 생성합니다. 서명은 토큰의 무결성을 확인하는 데 사용됩니다.

sequenceDiagram
  participant Client
  participant Server
  participant TokenManager
  Client->>Server: 로그인 요청 username, password
  Server->>TokenManager: 헤더 및 페이로드 생성
  TokenManager->>TokenManager: 서명 생성 Private Key/Secret Key 사용
  TokenManager->>Server: JWT 반환 헤더.페이로드.서명
  Server->>Client: JWT 반환
  Client->>Client: JWT 저장 local storage/cookies

3. 비대칭 암호화 (RS256)

비대칭 암호화는 RSA 알고리즘을 사용하여 두 개의 키 쌍(Private Key와 Public Key)을 사용합니다.

특징

  • Private Key: 토큰을 서명하는 데 사용되며, 서버에서만 알고 있어야 합니다.
  • Public Key: 토큰의 서명을 검증하는 데 사용되며, 클라이언트와 공유될 수 있습니다.
  • 보안성: Public Key를 통해 Private Key를 추출할 수 없기 때문에 높은 보안성을 제공합니다.

시퀀스 다이어그램

sequenceDiagram
  participant Client
  participant Server
  participant TokenManager
  Client->>Server: 로그인 요청 username, password
  Server->>TokenManager: 자격 증명 확인 및 JWT 생성 Private Key로 서명
  TokenManager->>Server: JWT 반환
  Server->>Client: JWT 반환
  Client->>Client: JWT 저장 local storage/cookies

  Client->>Server: Authorization 헤더에 JWT 포함 요청
  Server->>TokenManager: JWT 검증 Public Key로 서명 검증
  TokenManager->>Server: 서명 유효 시 요청 처리
  Server->>Client: 요청된 리소스 반환

TokenManager 클래스 예시

import jwt
from datetime import datetime, timedelta

class TokenManager:
    def __init__(self, private_key: str, public_key: str, access_token_expire_minutes: int, refresh_token_expire_days: int):
        self.private_key = private_key
        self.public_key = public_key
        self.access_token_expire_minutes = access_token_expire_minutes
        self.refresh_token_expire_days = refresh_token_expire_days

    def generate_token(self, user: dict) -> str:
        payload = {
            "user_id": user["user_id"],
            "exp": datetime.utcnow() + timedelta(minutes=self.access_token_expire_minutes),
            "iat": datetime.utcnow()
        }
        token = jwt.encode(payload, self.private_key, algorithm="RS256")
        return token

    def verify_token(self, token: str) -> dict:
        try:
            payload = jwt.decode(token, self.public_key, algorithms=["RS256"])
            return payload
        except jwt.ExpiredSignatureError:
            raise Exception("Token has expired")
        except jwt.InvalidTokenError:
            raise Exception("Invalid token")

4. 대칭 암호화 (HS256)

대칭 암호화는 하나의 비밀 키(Secret Key)를 사용하여 토큰을 서명하고 검증합니다.

특징

  • Secret Key: 토큰을 서명하고 검증하는 데 동일한 키를 사용합니다.
  • 보안성: 키가 노출되면 누구나 토큰을 생성하고 검증할 수 있기 때문에 비밀 키의 보안이 매우 중요합니다.

시퀀스 다이어그램

sequenceDiagram
  participant Client
  participant Server
  participant TokenManager
  Client->>Server: 로그인 요청 username, password
  Server->>TokenManager: 자격 증명 확인 및 JWT 생성 Secret Key로 서명
  TokenManager->>Server: JWT 반환
  Server->>Client: JWT 반환
  Client->>Client: JWT 저장 local storage/cookies

  Client->>Server: Authorization 헤더에 JWT 포함 요청
  Server->>TokenManager: JWT 검증 Secret Key로 서명 검증
  TokenManager->>Server: 서명 유효 시 요청 처리
  Server->>Client: 요청된 리소스 반환

TokenManager 클래스 예시

import jwt
from datetime import datetime, timedelta

class TokenManager:
    def __init__(self, secret_key: str, access_token_expire_minutes: int, refresh_token_expire_days: int):
        self.secret_key = secret_key
        self.access_token_expire_minutes = access_token_expire_minutes
        self.refresh_token_expire_days = refresh_token_expire_days

    def generate_token(self, user: dict) -> str:
        payload = {
            "user_id": user["user_id"],
            "exp": datetime.utcnow() + timedelta(minutes=self.access_token_expire_minutes),
            "iat": datetime.utcnow()
        }
        token = jwt.encode(payload, self.secret_key, algorithm="HS256")
        return token

    def verify_token(self, token: str) -> dict:
        try:
            payload = jwt.decode(token, self.secret_key, algorithms=["HS256"])
            return payload
        except jwt.ExpiredSignatureError:
            raise Exception("Token has expired")
        except jwt.InvalidTokenError:
            raise Exception("Invalid token")

5. 토큰 유형

JWT는 일반적으로 두 가지 유형의 토큰으로 사용됩니다: Access Token과 Refresh Token.

Access Token

Access Token은 사용자가 인증된 후, 특정 리소스에 접근하기 위해 사용하는 토큰입니다. 일반적으로 수명이 짧고, 만료 시간이 설정되어 있어 만료 후에는 재발급 받아야 합니다. Access Token은 클라이언트가 서버에 요청을 보낼 때마다 포함되어야 합니다.

Refresh Token

Refresh Token은 Access Token이 만료된 후, 새로운 Access Token을 발급받기 위해 사용하는 토큰입니다. Refresh Token은 수명이 더 길고, 주로 클라이언트의 보안 강화를 위해 사용됩니다. Refresh Token을 통해 새로운 Access Token을 발급받을 수 있습니다.

시퀀스 다이어그램

sequenceDiagram
  participant Client
  participant Server
  participant TokenManager
  Client->>Server: 로그인 요청 username, password
  Server->>TokenManager: Access Token 및 Refresh Token 생성 Secret Key로 서명
  TokenManager->>Server: Access Token 및 Refresh Token 반환
  Server->>Client: Access Token 및 Refresh Token 반환
  Client->>Client: Access Token 및 Refresh Token 저장 local storage/cookies

  Client->>Server: Authorization 헤더에 Access Token 포함 요청
  Server->>TokenManager: Access Token 검증 Secret Key로 서명 검증
  TokenManager->>Server: 서명 유효 시 요청 처리
  Server->>Client: 요청된 리소스 반환

  Client->>Server: Access Token 만료 시 Refresh Token으로 새로운 Access Token 요청
  Server->>TokenManager: Refresh Token 검증 Secret Key로 서명 검증
  TokenManager->>Server: 새로운 Access Token 반환
  Server->>Client: 새로운 Access Token 반환
  Client->>Client: 새로운 Access Token 저장 local storage/cookies

6. 결론

비대칭 암호화(RS256)와 대칭 암호화(HS256)는 각각 장단점이 있습니다.

  • RS256: Public Key를 통해 서명을 검증할 수 있으므로 보안성이 높습니다. Public Key를 여러 곳에 배포할 수 있어 키 관리가 용이합니다.
  • HS256: 하나의 Secret Key로 서명하고 검증하므로 구현이 간단합니다. 그러나 Secret Key가 노출되면 보안에 큰 위협이 됩니다.

JWT 암호화 방식을 선택할 때는 보안 요구 사항과 시스템 구조를 고려해야 합니다.

profile
AI Application Engineer

0개의 댓글