JWT 토큰 구조 — 헤더·페이로드·서명의 세 부분
JWT 토큰 구조 — 헤더·페이로드·서명의 세 부분
로그인한 사용자에게 발급되는 토큰을 열어 보면 점(.)으로 구분된 긴 문자열이 보이는데요. 그 문자열이 어떻게 만들어졌는지 알면 JWT가 왜 그렇게 생겼는지 자연스럽게 이해돼요. JWT는 RFC 7519로 표준화된, JSON 객체로 정보를 안전하게 주고받기 위한 열린 표준이에요. '컴팩트하고 스스로 완결된(self-contained)' 형태로, 서명되어 있어 위조 여부를 검증할 수 있는 게 핵심이에요.
본문
JWT는 크게 세 부분으로 나뉘고, 각 부분을 점(.)으로 구분해요. 이 컴팩트한 형태를 JWS Compact Serialization이라고 불러요.
xxxxx.yyyyy.zzzzz
- Header — 토큰의 타입과 서명에 쓸 알고리즘
- Payload — 사용자에 대한 정보(클레임)
- Signature — 헤더와 페이로드가 변조되지 않았음을 보증하는 서명
Header
헤더는 보통 두 가지를 담아요. 토큰의 타입이 JWT라는 사실과, 사용 중인 서명 알고리즘이요. HMAC SHA256이라면 alg로 HS256을 써요.
{
"alg": "HS256",
"typ": "JWT"
}
이 JSON을 Base64Url로 인코딩하면 JWT의 첫 번째 부분이 돼요.
Payload
두 번째 부분은 클레임(claims)을 담는 페이로드예요. 클레임은 주체(보통 사용자)에 대한 진술과 부가 데이터예요. 클레임은 등록(registered), 공개(public), 비공개(private) 세 종류로 나뉘어요.
- 등록 클레임: 미리 정의된 클레임으로 필수는 아니지만 권장돼요. 상호 운용에 유용한 값들로,
iss(issuer),exp(expiration time),sub(subject),aud(audience) 같은 것들이 있어요. JWT가 컴팩트하도록 클레임 이름은 세 글자로 짧게 유지돼요. - 공개 클레임: 사용하는 쪽이 자유롭게 정의할 수 있지만, 충돌을 피하려고 IANA JSON Web Token Registry에 등록하거나 충돌에 강한 네임스페이스를 가진 URI로 정의해요.
- 비공개 클레임: 당사자끼리 합의해서 만든 맞춤 클레임으로, 등록 클레임도 공개 클레임도 아니에요.
예시 페이로드예요.
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
페이로드도 Base64Url로 인코딩해 두 번째 부분을 만들어요.
서명된 토큰에서 이 정보는 변조는 방지되지만 누구나 읽을 수 있어요. 암호화되지 않은 JWT 헤더나 페이로드에 비밀 정보를 넣으면 안 되는 이유예요.
Signature
서명 부분은 인코딩된 헤더와 인코딩된 페이로드, 비밀 키(secret), 그리고 헤더에 지정된 알고리즘을 이용해 만들어요. HMAC SHA256이라면 다음과 같이 계산해요.
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
서명은 메시지가 전송 중에 바뀌지 않았는지 검증하는 데 쓰여요. 개인 키로 서명한 토큰이라면 보낸 사람이 바로 그 사람인지도 확인해 줘요.
모두 합치면
결과물은 점으로 구분된 세 개의 Base64-URL 문자열이에요. HTML과 HTTP 환경에서 쉽게 넘길 수 있고, SAML 같은 XML 기반 표준보다 컴팩트해요.