JWT 토큰 구조 — 헤더·페이로드·서명의 세 부분

JWT 토큰 구조 — 헤더·페이로드·서명의 세 부분

로그인한 사용자에게 발급되는 토큰을 열어 보면 점(.)으로 구분된 긴 문자열이 보이는데요. 그 문자열이 어떻게 만들어졌는지 알면 JWT가 왜 그렇게 생겼는지 자연스럽게 이해돼요. JWT는 RFC 7519로 표준화된, JSON 객체로 정보를 안전하게 주고받기 위한 열린 표준이에요. '컴팩트하고 스스로 완결된(self-contained)' 형태로, 서명되어 있어 위조 여부를 검증할 수 있는 게 핵심이에요.

출처: 공식문서 - Introduction to JSON Web Tokens

본문

JWT는 크게 세 부분으로 나뉘고, 각 부분을 점(.)으로 구분해요. 이 컴팩트한 형태를 JWS Compact Serialization이라고 불러요.

xxxxx.yyyyy.zzzzz
  • Header — 토큰의 타입과 서명에 쓸 알고리즘
  • Payload — 사용자에 대한 정보(클레임)
  • Signature — 헤더와 페이로드가 변조되지 않았음을 보증하는 서명

헤더는 보통 두 가지를 담아요. 토큰의 타입이 JWT라는 사실과, 사용 중인 서명 알고리즘이요. HMAC SHA256이라면 algHS256을 써요.

{
  "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 기반 표준보다 컴팩트해요.

더 알아보기