JWT(Json Web Token)이란?
JWT는 RFC 7519 에 등록된 표준으로서, JSON 포맷의 정보를 간결하게 전송할 수 있는 하나의 기술입니다.
사실 JWT라는 것은 추상적인 인터페이스의 역할이고 구현체는 두 가지로 분류되는데요.
- JWS(JSON Web Signature) - 서명 방식
- JWE(JSON Web Encryption) - 암호화 방식
대부분의 경우 JWT를 JWS와 동일한 의미로 사용하므로 이번 아티클에서도 서명 방식(JWS)를 기준으로 설명하겠습니다.
JWT는 서명(sign)이라는 개념을 사용해 데이터를 전송합니다. JWT를 처음 학습하시는 분들이라면 당연히 이 '서명'이라는 키워드가 낯설텐데, 쉽게 말해 데이터에 일련번호를 붙인다라고 생각하시면 될 것 같습니다. 이렇게 일련번호를 붙이는 특성을 갖고 있으니 데이터의 위변조를 쉽게 확인할 수 있는 것이죠.
그리고 이 때 JWT를 통해 전달되는 데이터를 클레임(claim)이라고 합니다. 클레임은 '주장한다' 라는 의미인데 JWT를 통해 전달되는 데이터들이 '데이터 공급자가 해당 정보가 진실임을 주장한다' 는 의미에서 유래되었다고 합니다. 그리고 이런 클레임들의 집합을 claim set이라고 하는데, 우리가 흔히 봐왔던 JSON과 동일한 구조입니다.
// claim
"name" : "teo"
// claim set
{
"name" : "teo",
"age" : 25
}
JWT는 이런 claim set(JSON)을 전송하는 용도로 사용됩니다. 그렇다면 언제 JWT가 유용하게 사용될 수 있을까요?
JWT의 용도
JWT 공식 사이트 introduction에 따르면, 다음과 같은 상황에서 JWT가 주로 사용된다고 합니다.
- 인증 및 인가
JWT를 사용할 수 있는 가장 대표적인 시나리오입니다. 한 번 사용자가 로그인하면 그 다음 요청부터는 JWT를 함께 전송하게 되고, 이를 통해 인증 및 인가 작업을 수행할 수 있습니다.
- 정보 교환
JWT는 정보 교환에 있어서도 자주 사용됩니다. JWT는 서명(sign)될 수 있기 때문에 이 데이터를 보낸 사람이 누구인지 알 수 있고, 데이터가 위 변조되지 않았음을 알 수 있습니다.
JWT 구조
JWT는 하나의 긴 문자열이지만, 총 세 가지 부분으로 나누어집니다.
- Header
- Payload
- Signature
그리고 Header, Payload, Signature 사이는 '.' 으로 구분합니다. 예를 들어, JWT는 다음과 같은 형식을 띱니다.
이제 각각의 부분들(Header, Payload, Signature)에 대해 알아보겠습니다.
Header
헤더는 일반적으로 두 부분으로 구성됩니다. 바로 1. 토큰의 타입과, 2. 서명 알고리즘입니다.
{
"alg": "HS256",
"typ": "JWT"
}
'typ'가 바로 'type', 즉 토큰의 타입을 의미하는데 typ 값을 통해 해당 토큰이 JWT 토큰임을 명시적으로 드러낼 수 있습니다. 필수 값은 아니지만 JWT 표준에서는 JWT 토큰이라면 'typ'을 'JWT'로 지정하는 것을 권장하고 있습니다.
'alg'는 필수로 지정해야 하는 값이고 서명 알고리즘을 의미하는데 HMAC SHA 256이나 RSA 등과 같은 알고리즘이 포함될 수 있습니다. 구체적으로 지원하는 알고리즘이 무엇이 있는지는 다음 링크에서 확인하실 수 있습니다. [RFC 7518 3.1]
Payload
payload는 클레임(claim)들을 저장하고 있는 부분입니다. 실제 데이터가 담길 위치라고 볼 수 있는 것이죠. 그리고 payload는 크게 세 종류로 구분되는데요, 등록된 클레임(Registered claims), 공개 클레임(Public claims), 비공개 클레임(Private claims)입니다.
참고로 payload는 '화물' 이라는 뜻을 가지고 있습니다.
- 등록된 클레임(Registered claims)
IANA에 미리 등록된 클레임들을 의미합니다. 쉽게 말해 '예약어' 쯤으로 생각하셔도 될 것 같습니다. 'iss', 'exp', 'sub', 'aud' 등이 있으며, 필수 입력 값은 아닙니다. 상호운용성이 필요한 경우 상황에 따라 적절하게 사용하면 될 것 같습니다.
{
"sub": "this is my token",
"iss" : "teo"
}
- 공개 클레임(Public claims)
JWT를 사용하는 사람이라면 누구든지 정의할 수 있는 클레임들을 의미합니다. 다만 IANA JWT Registry 를 확인해서 동일한 이름을 가진 클레임이 있는지는 확인해야 합니다. 등록된 클레임들과의 충돌 위험이 있으니까요. 아니면 URI 형식으로 클레임명을 사용해 충돌을 회피하는 방법도 존재합니다.
{
"https://example.com/claims/user_id" : 12345
}
- 비공개 클레임(private claims)
웹 어플리케이션을 만들면 주로 사용하게 되는 것이 비공개 클레임입니다. 등록된 클레임이 아니면서 공개 클레임도 아닌 클레임들을 의미합니다. 비공개 클레임은 주로 특정 비즈니스에 종속적인 의미를 담을 때 사용됩니다. 다만 비공개 클레임도 등록된 클레임들과의 충돌 위험은 존재하기 때문에 필요에 따라서는 URI 방식을 사용하거나, 네임스페이스를 사용하는 등의 충돌 회피 전략을 사용해야 합니다.
{
"department" : "HR",
"position" : "Manager"
}
Signature
JWT에서 핵심이 되는 부분입니다. 이 서명(Signature) 부분을 통해 데이터가 변하지 않았음을 알 수 있고, private key로 서명된 경우에는 전송자가 누구인지까지 알 수 있습니다. HMAC SHA256 알고리즘을 사용하는 경우에는 다음과 같은 형태를 띠는데요,
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
헤더를 인코딩 한 값 + '.' + 페이로드를 인코딩한 값 + 시크릿 키로 구성됩니다. 참고로, 서명(Signature) 부분에서 사용되는 알고리즘은 헤더(Header)에서 작성한 'alg' 값입니다.
JWT는 이처럼 Header, Payload, Signature 부분으로 구성됩니다. 이 세 부분을 모두 합치면 다음과 같은 형태가 됩니다.
참고로 header, payload, signature 모두 base64Url 방식으로 인코딩됩니다. 즉, 다음과 같은 형태를 띱니다.
base64Url(header).base64Url(payload).base64Url(signature)
반대로 이야기하면 base64Url 디코딩 방식만 안다면 JWT의 모든 정보를 확인할 수 있다는 것을 의미합니다. JWT를 사용할 때 민감한 정보를 payload에 넣어서는 안된다고 하는 이유도 이 때문입니다. 애초에 암호화가 목적이 아니니까요. 암호화가 목적이라면 JWS가 아닌 JWE를 고려하시는 것을 추천드립니다.
마치며
JWT란 무엇인지, 그리고 구조는 어떻게 되어 있는지에 대해 간략하게 알아보았습니다. 기본적인 내용만 정리한 터라 아티클에서 다루지 못한 내용도 많은데, 조금 더 깊은 내용을 공부하시길 원하신다면 표준 문서를 읽어보시는 것을 권장합니다. 감사합니다.
참고 자료
https://datatracker.ietf.org/doc/html/rfc7519
https://www.iana.org/assignments/jwt/jwt.xhtml
https://velog.io/@dae-hwa/JWTJSON-Web-Token-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0
'웹' 카테고리의 다른 글
[Tomcat] maxThreads, maxConnections, acceptCount로 Tomcat 튜닝하기 (10) | 2023.09.08 |
---|---|
SOP(Same Origin Policy)와 CORS(Cross Origin Resource Sharing) (0) | 2023.08.22 |
세션 방식 인증과 토큰 방식 인증 알아보기 (2) | 2023.07.09 |
브라우저 저장소(쿠키, 로컬 스토리지, 세션 스토리지) 개념 알아보기 (1) | 2023.07.08 |