JWT 디코더

토큰 안에 어떤 정보가 담겨 있는지 서버 없이 브라우저에서 바로 확인하세요.

JWT의 구조: 헤더.페이로드.서명

JWT(JSON Web Token, RFC 7519)는 점(.)으로 이어진 세 덩어리입니다. 앞의 두 덩어리는 JSON을 Base64URL로 인코딩한 것이고, 세 번째는 서명입니다. 이 페이지의 데모 토큰을 나눠 보면 다음과 같습니다.

부분내용데모 값 (Base64URL)
헤더알고리즘(alg), 타입(typ) 등 메타데이터eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 = {"alg":"HS256","typ":"JWT"}
페이로드클레임(sub, iat, exp, 사용자 정의 값)eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Iu2Zjeq4uOuPmSIsImlhdCI6MTcwMDAwMDAwMCwiZXhwIjoxNzAwMDAzNjAwfQ
서명"헤더.페이로드" 문자열에 대한 HMAC 또는 전자서명h3A987M6nrZ30134c0r8JMhI-ZWzw-bFETtqHAVm5sw

서명 대상은 정확히 Base64URL(헤더) + "." + Base64URL(페이로드) 문자열입니다. 서명이 가리키는 것은 JSON의 의미가 아니라 인코딩된 바이트 그대로이므로, 공백 하나만 달라도 서명은 달라집니다. HS256의 서명은 이 문자열에 비밀키로 HMAC-SHA-256을 계산한 결과입니다. 위 데모 토큰은 빌드할 때 비밀키 playinfobox-demo-secret으로 실제 계산해 넣은 값이라, 위 "서명 검증" 버튼으로 통과를 확인할 수 있습니다.

Base64URL은 일반 Base64와 무엇이 다른가

표준 Base64는 URL이나 헤더에서 문제가 되는 +, /, 끝의 = 패딩을 씁니다. Base64URL은+를 -로, /를 _로 바꾸고 패딩을 생략합니다. 예를 들어 바이트 0xFB 0xFF는 Base64로 +/8=, Base64URL로 -_8입니다. 그래서 JWT를 일반 Base64 디코더에 넣으면 -, _ 때문에 실패하거나 글자가 깨질 수 있습니다. 이 도구는 문자를 되돌리고 길이가 4의 배수가 되도록 =를 채운 뒤 UTF-8로 해석합니다. 일반 Base64 인코딩·디코딩은 Base64 변환기에서 할 수 있습니다.

디코딩은 검증이 아닙니다

페이로드는 암호화가 아니라 인코딩입니다. 토큰을 가진 사람은 누구나 내용을 읽을 수 있으므로 비밀번호, 주민등록번호, 카드번호 같은 값을 넣으면 안 됩니다. 또한 디코딩해서 이름이나 권한이 보인다는 사실은 그 토큰이 진짜라는 증거가 아닙니다. 페이로드는 누구나 마음대로 써서 인코딩할 수 있기 때문입니다. 신뢰 여부는 서명 검증으로만 판단합니다. 서버(API)는 요청이 올 때마다 다음을 모두 확인해야 합니다.

확실히 알아야 할 보안 주의점

서명 길이로 알고리즘 짐작하기

서명 부분의 길이는 알고리즘이 정합니다. 바이트 수 n의 Base64URL(패딩 없음) 문자 수는 ceil(4n/3)입니다. 이 도구는 서명 길이를 계산해 헤더의 alg와 맞는지 알려 줍니다.

alg방식서명 바이트Base64URL 문자 수
HS256HMAC-SHA-2563243
HS384HMAC-SHA-3844864
HS512HMAC-SHA-5126486
ES256ECDSA P-256 (r‖s 고정 길이)6486
ES384ECDSA P-38496128
ES512ECDSA P-521132176
RS256RSASSA-PKCS1-v1_5, 2048비트 키256342

RS256의 서명 길이는 키 크기와 같으므로 3072비트 키라면 384바이트입니다. 표의 값은 2048비트 기준입니다. ES256 등 ECDSA의 JWT 서명은 DER가 아니라 r과 s를 이어 붙인 고정 길이 형식(RFC 7518)입니다.

등록 클레임: exp, nbf, iat 읽는 법

시간 클레임은 1970-01-01 00:00:00 UTC 기준 초 단위의 정수(NumericDate)입니다. 밀리초가 아닙니다. 그래서 값이 13자리 정도로 크면(1,000,000,000,000 이상) 밀리초를 잘못 넣은 토큰일 가능성이 높고, 이 도구는 이를 경고합니다. 변환 원리는 Unix 타임스탬프 변환기에서 직접 확인해 볼 수 있습니다.

클레임의미판정 기준
iss발급자기대하는 발급자와 일치해야 함
sub주체(사용자 ID 등)발급자 범위에서 고유
aud대상 서비스내 서비스가 대상에 포함되어야 함
exp만료 시각현재 시각이 exp 이상이면 거부 (RFC 7519: 현재 시각이 exp보다 이전이어야 처리 가능)
nbf이 시각 전에는 무효현재 시각이 nbf 이전이면 거부
iat발급 시각토큰 나이 계산용
jti토큰 고유 ID재사용·폐기 목록 관리에 사용. UUID를 흔히 씀

서버 간 시계가 몇 초 어긋날 수 있어 실무에서는 검증 시 수십 초 정도의 여유(leeway)를 두기도 합니다. 이 도구의 만료 표시는 열어 둔 브라우저 시계를 기준으로 단순 비교하며, 여유 시간은 반영하지 않습니다.

검산 예시

예시 1 (만료 계산): 데모 토큰의 iat는 1700000000, exp는 1700003600입니다. 두 값의 차이는 3600초 = 1시간이고, 1700000000은 2023-11-14 22:13:20 UTC입니다. 따라서 이 토큰은 1시간 수명이었고 지금은 이미 만료되었습니다. 만료 상태에서도 서명은 올바르므로 "서명 유효 + 만료됨"이 동시에 나올 수 있다는 점이 디코딩·검증·만료 판단이 서로 다른 단계임을 보여 줍니다.

예시 2 (밀리초 오류): 어떤 토큰의 exp가 1700003600000이라면 초로 해석할 때 서기 55,000년대의 먼 미래가 됩니다. 이는 사실상 만료 없는 토큰이므로 값을 1000으로 나눈 초 단위로 바로잡아야 합니다.

이 도구를 쓸 때의 한계

자주 묻는 질문

입력한 JWT가 서버로 전송되나요?

아닙니다. 모든 디코딩과 서명 계산은 브라우저 안에서만 이뤄지며, 입력한 토큰 값은 어디로도 전송되지 않습니다. 다만 실제 서비스에서 발급된 토큰이라면 화면 공유나 캡처 시 노출되지 않도록 주의하는 것이 좋습니다.

왜 3부분(점 2개)으로 나뉘어 있나요?

JWT는 헤더.페이로드.서명 순서로 점(.)을 구분자로 이어붙인 구조입니다. 헤더에는 알고리즘 정보, 페이로드에는 실제 담고 있는 데이터(클레임), 서명에는 헤더와 페이로드가 변조되지 않았음을 증명하는 값이 들어 있습니다.

디코딩되면 토큰이 유효한 건가요?

아닙니다. 디코딩은 누구나 할 수 있는 읽기 동작일 뿐입니다. 유효성은 서명, 만료(exp), 발급자(iss), 대상(aud)을 서버가 직접 검증해야 확정됩니다.

서명 부분이 비어 있는 토큰은 무엇인가요?

alg가 none인 Unsecured JWT이며 끝이 점(.)으로 끝납니다. 일반적인 인증에는 쓰면 안 되고, 서버는 이를 거부하도록 설정해야 합니다.

페이로드를 수정하면 어떻게 되나요?

페이로드 문자가 한 글자라도 바뀌면 "헤더.페이로드" 문자열이 달라지므로 기존 서명으로는 검증에 실패합니다. 비밀키(또는 개인키)가 없으면 올바른 서명을 새로 만들 수 없습니다. 이 구조가 JWT가 변조를 막는 방식입니다.

한글이 깨지지 않고 나오는 이유는?

JWT의 JSON은 UTF-8 바이트를 Base64URL로 변환한 것입니다. 이 도구는 atob 결과의 바이트를 UTF-8로 해석하므로 한글 클레임도 정상 표시됩니다. 일부 구형 디코더는 이 과정을 생략해 한글이 깨집니다.