[기술 딥다이브] JWT: 토큰을 잘라 보면 보이는 것
한 줄 요약: JWT는 점으로 구분된 header·payload·signature 세 부분이고, 앞 두 부분은 누구나 디코딩할 수 있으며, 진짜 안전장치는 서버 비밀키로 만든 세 번째 서명이다.
로그인하면 브라우저에 긴 문자열 하나가 저장돼요. 점(.) 두 개로 세 동강 난 그 문자열이 바로 JWT(JSON Web Token)예요. 겉보기엔 암호문 같지만, 잘라 보면 의외로 투명해요. 실제로 디코딩해 가며 구조를 풀어 설명하겠어요.
토큰 해부도: 세 부분의 역할
아래 도식은 JWT의 세 부분과 각각이 무엇을 담는지 보여줘요.
실제로 디코딩해 보기: 10초짜리 실험
JWT의 앞 두 부분은 그냥 base64url 인코딩일 뿐이라 터미널에서 바로 풀려요. 직접 만든 토큰을 디코딩한 실제 출력이에요:
토큰: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkRvaHl1biIsImlhdCI6MTcwMDAwMDAwMH0
.xN_eA…(서명 부분)
$ echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | base64 -d
{"alg":"HS256","typ":"JWT"} ← header
$ echo "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkRvaHl1biIsImlhdCI6MTcwMDAwMDAwMH0" | base64 -d
{"sub":"1234567890","name":"Dohyun","iat":1700000000} ← payload
header는 ‘어떤 알고리즘으로 서명했는가’(alg: HS256)를, payload는 ‘이 토큰이 누구에 대한 것인가’(sub=사용자 ID, name, 발행 시각 iat)를 담아요. 여기서 중요한 사실 하나가 나와요: 토큰 안에 누가 적혀 있는지는 누구나 볼 수 있어요. JWT를 ’암호화’라고 오해하는 경우가 많은데, JWT는 암호화가 아니라 서명 기술이에요. 내용을 숨기는 것이 아니라 내용이 변조되지 않았다는 것을 증명해요.
서명: payload를 한 글자 바꾸면 어떻게 되나
signature는 다음 계산의 결과예요:
signature = HMAC-SHA256( base64url(header) + "." + base64url(payload), 비밀키 )
누군가 payload의 name을 Dohyun에서 Admin으로 바꾸면 어떻게 될까요? payload가 바뀌었으므로 위 계산 결과도 완전히 달라져요. 그런데 공격자는 서명을 다시 만들 비밀키가 없어요. 서버가 토큰을 받아 같은 계산을 해 보면 저장된 서명과 일치하지 않아 401로 거절해요. base64는 인코딩일 뿐이므로 누구나 풀 수 있지만, 비밀키 없이는 서명을 맞춰 쓸 수 없다는 것이 JWT 보안의 전부예요.
iat, exp: 시간이 들어있는 토큰
payload의 표준 필드 몇 개만 알면 토큰 읽기가 끝나요. iat(issued at, 발행 시각), exp(만료 시각), sub(subject, 주체 ID)가 대표적이에요. 서버는 로그인 상태를 자기 메모리에 두지 않고 토큰의 exp만 확인해요. 서버가 세션을 기억하지 않아도 되므로 서버 여러 대가 아무렇게나 나눠 받아도 같은 토큰을 검증할 수 있어요. 이 ’서버가 상태를 안 갖는다’는 성질(stateless)이 JWT가 마이크로서비스 시대에 널리 퍼진 이유예요.
만료와 재발급: 왜 토큰은 짧게 살아야 하나
한 번 유출된 토큰은 서명이 유효한 동안 계속 쓸 수 있어요. 그래서 JWT는 보통 짧은 수명(수십 분)을 주고, 만료되면 refresh token으로 재발급해요. ’비밀키를 모르면 위조할 수 없다’는 장점과 ’한 번 유출되면 만료까지는 회수할 수 없다’는 한계가 한 구조에서 나와요. 그래서 로그아웃 시 브라우저에서 토큰을 지워도, 이미 유출된 토큰까지 사라지는 것은 아니라는 점을 이해해야 해요.
어떻게 연결되는가
한 줄로 요약하면: JWT는 header(서명 알고리즘)·payload(주장하는 내용)·signature(변조 방지 증명) 세 부분이고, 앞 두 부분은 투명하며 보안은 비밀키 서명에 있다는 구조예요. 인코딩은 암호화가 아니라는 것, 그리고 서명이 만드는 ’변조 불가능성’이 JWT의 본질이에요. 참고로 payload의 JSON은 UTF-8로 인코딩된 바이트를 base64url로 감싼 것이므로, 한글 이름이 들어간 토큰의 인코딩 규칙도 UTF-8 글과 연결돼요.
출처: RFC 7519: JWT · jwt.io 소개 · 위키백과: JSON Web Token
이미지: 위키미디어 공용 — JWT 구조도