Programing

JWT(JSON Web Token)

데브호호 2024. 10. 2. 09:00

1. JWT란 무엇인가?

JSON Web Token(JWT)은 당사자 간에 정보를 안전하게 전송하기 위한 개방형 표준(RFC 7519)입니다. 이 정보는 디지털 서명되어 있어 신뢰할 수 있습니다. JWTs는 HMAC 알고리즘을 사용하거나 RSA 또는 ECDSA를 사용하는 공개/개인 키 쌍을 사용하여 서명할 수 있습니다.

JWT의 구조

JWT는 세 부분으로 구성됩니다:

  1. 헤더 (Header)
  2. 페이로드 (Payload)
  3. 서명 (Signature)

각 부분은 점(.)으로 구분되며, Base64Url로 인코딩됩니다.

xxxxx.yyyyy.zzzzz

예를 들어, 실제 JWT는 다음과 같이 보일 수 있습니다:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

헤더(Header)

헤더는 두 가지 정보를 포함합니다

  • typ: 토큰의 타입을 지정 (JWT)
  • alg: 해싱 알고리즘을 지정 (보통 HMAC SHA256 또는 RSA)

예시:

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

페이로드(Payload)

페이로드는 토큰에 담을 클레임(claim) 정보를 포함합니다. 클레임은 엔티티(일반적으로 사용자)와 추가 데이터에 대한 설명입니다. 클레임의 종류는 다음과 같습니다:

  • 등록된 클레임
  • 공개 클레임
  • 비공개 클레임

예시:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

서명(Signature)

서명은 헤더의 인코딩값과 페이로드의 인코딩값을 합친 후 비밀키로 해싱하여 생성됩니다. 서명은 토큰이 변조되지 않았음을 확인하는 데 사용됩니다.

 

2. JWT의 작동 방식

  1. 사용자가 자격 증명으로 로그인합니다.
  2. 서버가 사용자 정보로 JWT를 생성합니다.
  3. 서버가 JWT를 클라이언트에 반환합니다.
  4. 클라이언트는 이후의 요청에 이 JWT를 포함시킵니다.
  5. 서버는 JWT를 검증하고 요청을 처리합니다.

3. JWT의 장점

  1. 무상태성(Stateless): 서버 측에서 세션을 저장할 필요가 없어 서버 부하를 줄일 수 있습니다.
  2. 확장성: 다중 서버 환경에서도 쉽게 사용할 수 있습니다.
  3. 유연성: 다양한 프로그래밍 언어에서 지원됩니다.
  4. 자가 수용적(Self-contained): 토큰 자체에 필요한 모든 정보가 포함되어 있습니다.

4. JWT의 단점 및 보안 고려사항

4.1 크기 문제

JWT는 일반 세션 ID에 비해 크기가 큽니다. 예를 들어:

import jwt

# JWT 생성
jwt_token = jwt.encode({"user_id": "xiaou"}, "secret", algorithm="HS256")
print(f"JWT 크기: {len(jwt_token)} bytes")

# 일반 세션 ID
session_id = "xiaou"
print(f"세션 ID 크기: {len(session_id)} bytes")

이 코드를 실행하면 JWT가 일반 세션 ID보다 훨씬 크다는 것을 알 수 있습니다.

4.2 중복 서명

많은 웹 프레임워크에서 이미 쿠키에 서명을 제공하고 있어, JWT를 사용하면 중복 서명이 발생할 수 있습니다.

4.3 토큰 취소 문제

JWT는 발급 후 만료 시간까지 유효하므로, 즉시 취소가 어렵습니다. 이는 보안 위험을 초래할 수 있습니다.

import jwt
from datetime import datetime, timedelta

# JWT 생성 (만료 시간 30분)
expiration_time = datetime.utcnow() + timedelta(minutes=30)
jwt_token = jwt.encode({"user_id": "xiaou", "exp": expiration_time}, "secret", algorithm="HS256")

# 로그아웃 시도
# 주의: 이 방식으로는 토큰을 즉시 무효화할 수 없음
print("로그아웃 시도 - 하지만 토큰은 여전히 유효함")

# 토큰 검증 (여전히 유효)
try:
    decoded = jwt.decode(jwt_token, "secret", algorithms=["HS256"])
    print("토큰이 여전히 유효함:", decoded)
except jwt.ExpiredSignatureError:
    print("토큰이 만료됨")

4.4 데이터 최신성 문제

사용자의 권한이 변경되어도, 기존 토큰은 만료 전까지 이전 권한을 유지합니다.

4.5 암호화 부재

기본적으로 JWT는 서명만 되고 암호화되지 않아, 중간자 공격에 취약할 수 있습니다.

import jwt

# JWT 생성 (암호화되지 않음)
jwt_token = jwt.encode({"user_id": "xiaou", "role": "admin"}, "secret", algorithm="HS256")

# JWT 내용 확인 (누구나 디코딩 가능)
decoded = jwt.decode(jwt_token, options={"verify_signature": False})
print("JWT 내용 (암호화되지 않음):", decoded)

5. 결론 및 권장 사항

JWT는 일회성 인증 토큰으로는 적합하지만, 장기적인 세션 관리에는 적합하지 않을 수 있습니다. 운영 환경에서는 다음을 고려해야 합니다:

  1. 세션 관리: 전통적인 서버 측 세션을 고려하세요.
  2. 토큰 수명: JWT를 사용한다면, 짧은 만료 시간을 설정하세요.
  3. 암호화: 필요한 경우 JWE(JSON Web Encryption)를 사용하여 토큰을 암호화하세요.
  4. 토큰 저장: 클라이언트 측에서 토큰을 안전하게 저장하세요 (예: HttpOnly 쿠키).
  5. 갱신 전략: 리프레시 토큰을 사용하여 보안을 강화하세요.

JWT는 강력한 도구이지만, 적절한 사용 사례와 보안 고려사항을 신중히 검토해야 합니다. 개발 목적으로는 학습에 유용할 수 있지만, 운영 환경에서는 보안 전문가와 상의하여 최적의 인증 전략을 선택하는 것이 좋습니다.