JWT 초보자를 위한 설명 — 간단한 수학 비유와 함께
Source: Dev.to
소개
사용자가 로그인하면 서버는 모든 요청마다 사용자가 누구인지 기억해야 합니다. 기존 세션은 데이터를 서버에 저장하는 반면, JWT(JSON Web Token)는 사용자 정보(예: 사용자 ID, 역할, 만료 시간)를 토큰 자체에 담습니다. 서버는 각 요청에서 서명만 검증하면 되므로 데이터베이스 조회를 피할 수 있습니다.
JWT 구조
JWT는 점(.)으로 연결된 세 부분으로 구성됩니다:
header.payload.signature
헤더
헤더는 서명 알고리즘(예: HS256 또는 RS256)과 토큰 타입을 지정합니다.
페이로드
페이로드에는 실제 사용자 데이터인 클레임이 들어갑니다. 일반적인 클레임은 다음과 같습니다:
sub→ Subject (사용자 ID)iss→ Issuer (발행자)aud→ Audience (대상)exp→ Expiration time (만료 시간)iat→ Issued at (발행 시점)
서명
서명은 토큰의 무결성을 보장합니다. 서명을 만들기 위해 서버는:
- 헤더와 페이로드를 Base64‑인코딩합니다.
- 점(
.)으로 연결해 메시지를 만듭니다. - 비밀 키(또는 개인 키)와 함께 해시 함수를 적용해 서명을 생성합니다.
그 후 서명을 토큰에 붙입니다.
토큰 검증
1. HS256 (대칭)
토큰 생성과 검증 모두 같은 비밀 키를 사용합니다.
비유:
- 비밀 = 3
- 서명 규칙 = 비밀로 곱하기
토큰 생성
message = 10
signature = message * secret # 10 * 3 = 30
token = 10.30
토큰 검증
received_token = 10.30
recalculated_signature = 10 * 3 # 30
recalculated_signature == 30 # → valid
공격자가 메시지를 바꾸면:
received_token = 18.30
recalculated_signature = 18 * 3 # 36
recalculated_signature == 30 # → invalid
2. RS256 (비대칭)
서버는 개인 키로 서명하고, 검증은 공개 키로 수행합니다. 이를 통해 여러 서비스가 개인 키를 공유하지 않고도 토큰을 검증할 수 있습니다.
단순화된 비유(실제 RSA는 아님):
Private key operation = multiply by 3
Public key operation = divide by 3
서명 (토큰 생성)
message = 10
signature = message * 3 # 30
token = 10.30
검증
received_token = 10.30
expected_message = signature / 3 # 30 / 3 = 10
expected_message == 10 # → valid
메시지 변조
received_token = 20.30
expected_message = 30 / 3 # 10
expected_message == 20 # → invalid
잘못된 공개 키
public_key = divide by 5
expected_message = 30 / 5 # 6
expected_message == 10 # → invalid
중요한 보안 주의사항
페이로드는 Base64‑인코딩만 되어 있을 뿐, 암호화되지 않았습니다. 누구든지 디코딩해서 내용을 읽을 수 있습니다. 따라서 JWT 안에 민감한 데이터(예: 비밀번호)를 절대 저장하지 마세요. 서명은 토큰이 변조되지 않았음을 보장하지만, 데이터를 숨기지는 않습니다.