JWT(JSON Web Token)
1. JWT란 무엇인가?
JSON Web Token(JWT)은 당사자 간에 정보를 안전하게 전송하기 위한 개방형 표준(RFC 7519)입니다. 이 정보는 디지털 서명되어 있어 신뢰할 수 있습니다. JWTs는 HMAC 알고리즘을 사용하거나 RSA 또는 ECDSA를 사용하는 공개/개인 키 쌍을 사용하여 서명할 수 있습니다.
JWT의 구조
JWT는 세 부분으로 구성됩니다:
- 헤더 (Header)
- 페이로드 (Payload)
- 서명 (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의 작동 방식
- 사용자가 자격 증명으로 로그인합니다.
- 서버가 사용자 정보로 JWT를 생성합니다.
- 서버가 JWT를 클라이언트에 반환합니다.
- 클라이언트는 이후의 요청에 이 JWT를 포함시킵니다.
- 서버는 JWT를 검증하고 요청을 처리합니다.
3. JWT의 장점
- 무상태성(Stateless): 서버 측에서 세션을 저장할 필요가 없어 서버 부하를 줄일 수 있습니다.
- 확장성: 다중 서버 환경에서도 쉽게 사용할 수 있습니다.
- 유연성: 다양한 프로그래밍 언어에서 지원됩니다.
- 자가 수용적(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는 일회성 인증 토큰으로는 적합하지만, 장기적인 세션 관리에는 적합하지 않을 수 있습니다. 운영 환경에서는 다음을 고려해야 합니다:
- 세션 관리: 전통적인 서버 측 세션을 고려하세요.
- 토큰 수명: JWT를 사용한다면, 짧은 만료 시간을 설정하세요.
- 암호화: 필요한 경우 JWE(JSON Web Encryption)를 사용하여 토큰을 암호화하세요.
- 토큰 저장: 클라이언트 측에서 토큰을 안전하게 저장하세요 (예: HttpOnly 쿠키).
- 갱신 전략: 리프레시 토큰을 사용하여 보안을 강화하세요.
JWT는 강력한 도구이지만, 적절한 사용 사례와 보안 고려사항을 신중히 검토해야 합니다. 개발 목적으로는 학습에 유용할 수 있지만, 운영 환경에서는 보안 전문가와 상의하여 최적의 인증 전략을 선택하는 것이 좋습니다.