Reading JWTs safely — what a decoder shows and what it does not
A JWT is three Base64url segments joined with dots: header.payload.signature. Any decoder — including the one on this site — pulls the header and payload out of those segments and shows them to you as JSON. That is genuinely useful for debugging. It is also the single most misunderstood thing about the format.
Decoding is not validation
The decoder did not check the signature. It did not check that the token was signed by someone you trust. It did not check that the token is still within its validity window. It showed you what is inside the envelope, nothing more. If your application code accepts a token because the payload looks right, you have a vulnerability — an attacker can forge a payload with whatever claims they like, encode it, and your code will accept it.
Validation is a separate step that requires the issuer's public key (for asymmetric algorithms like RS256/ES256) or shared secret (for symmetric HS256). Real validation:
- Recomputes the signature with the same key and compares it constant-time.
- Checks the
expclaim against the current time, with a small allowed clock skew. - Checks the
iss,aud, andnbfclaims if your protocol uses them. - Refuses tokens whose
algheader isnoneor unexpected.
The classic mistakes
- Trusting the
algheader. If your verification library lets the token tell it which algorithm to use, an attacker can switchalgtononeand skip the signature entirely. Pin the algorithm on the server side. - Treating JWTs as opaque session tokens. A JWT is signed, not encrypted. Anyone who can see the token can read its payload. Do not put data in there that you would not show the user.
- Storing access tokens in localStorage. Any XSS on your site reads localStorage. HttpOnly cookies are not perfect, but they are not readable from JavaScript.
- Long expiries with no rotation. A leaked JWT is valid until it expires — there is no server-side session to revoke. Keep
expshort and pair access tokens with rotating refresh tokens. - Ignoring
kid. Production systems rotate signing keys. Thekidheader tells you which key signed the token. Verifiers that hard-code a single key break the first time keys rotate.
What the decoder is good for
Debugging. When a user reports "the API says I am not allowed," paste their token, look at the claims, and check the expiry. When a third-party integration sends you a token and you want to know what is in it. When you are reading a tutorial that mentions a claim like scope and you want to see how your auth provider populates it. The decoder runs locally in your browser — the pasted token is never sent anywhere — so it is safe to use with production tokens you would not paste into a random web form.
JWT를 안전하게 다루는 법 — 디코더가 보여주는 것과 보여주지 않는 것
JWT는 점(.)으로 연결된 세 개의 Base64url 세그먼트입니다 — 헤더.페이로드.서명. 어떤 디코더든 — 이 사이트의 디코더 포함 — 앞 두 세그먼트에서 헤더와 페이로드를 추출해 JSON으로 보여줍니다. 디버깅에는 정말 유용하지만, 동시에 이 포맷에 대해 가장 많이 오해받는 부분이기도 합니다.
디코딩은 검증이 아니다
디코더는 서명을 확인하지 않았습니다. 신뢰하는 발급자가 서명했는지도, 토큰이 아직 유효 기간 안에 있는지도 확인하지 않았습니다. 그저 봉투 안에 든 내용을 펼쳐 보여줬을 뿐입니다. 만약 애플리케이션 코드가 페이로드가 "그럴듯해 보인다"는 이유로 토큰을 받아들인다면, 그 코드는 취약점입니다 — 공격자가 원하는 클레임으로 페이로드를 위조해서 인코딩하면 그대로 받아들여집니다.
검증은 디코딩과는 별개의 단계이며, 비대칭 알고리즘(RS256/ES256)이라면 발급자의 공개 키, 대칭 알고리즘(HS256)이라면 공유 시크릿이 필요합니다. 실제 검증이 하는 일:
- 같은 키로 서명을 다시 계산하고 상수 시간(constant-time)으로 비교합니다.
exp클레임을 현재 시각과 비교합니다. 시계 오차를 약간 허용합니다.- 프로토콜에서 사용한다면
iss,aud,nbf클레임도 검증합니다. alg헤더가none이거나 예상과 다른 값이면 토큰을 거부합니다.
자주 하는 실수
alg헤더를 그대로 신뢰하기. 검증 라이브러리가 토큰이 말하는 알고리즘을 그대로 따른다면, 공격자가alg를none으로 바꿔 서명을 통째로 건너뛸 수 있습니다. 알고리즘은 서버에서 고정해야 합니다.- JWT를 불투명한 세션 토큰처럼 다루기. JWT는 서명되어 있을 뿐 암호화되어 있지 않습니다. 토큰을 볼 수 있는 사람은 누구나 페이로드를 읽을 수 있습니다. 사용자에게 보여주지 못할 데이터는 넣지 마세요.
- 액세스 토큰을 localStorage에 저장하기. 사이트에 XSS가 한 번이라도 생기면 localStorage는 그대로 읽힙니다. HttpOnly 쿠키도 완벽하지는 않지만 자바스크립트에서 읽을 수 없다는 점에서 더 낫습니다.
- 긴 만료 시간 + 로테이션 없음. 유출된 JWT는 만료될 때까지 유효합니다 — 서버에 세션 상태가 없어서 무효화할 방법이 없습니다.
exp는 짧게 잡고, 액세스 토큰은 로테이션되는 리프레시 토큰과 짝지어 쓰세요. kid를 무시하기. 운영 시스템은 서명 키를 주기적으로 로테이션합니다.kid헤더가 어떤 키로 서명되었는지 알려줍니다. 키를 하드 코딩한 검증 코드는 키 로테이션이 일어나는 순간 그대로 깨집니다.
그럼 디코더는 언제 쓰나
디버깅입니다. 사용자가 "API가 권한 없음이라고 합니다"라고 신고했을 때 토큰을 붙여 넣고 클레임을 확인하고 만료 시각을 보는 용도. 외부 서비스가 보낸 토큰에 어떤 정보가 들어 있는지 확인할 때. 문서에 등장한 scope 같은 클레임을 인증 공급자가 어떻게 채우는지 보고 싶을 때. 디코더는 브라우저 안에서만 동작합니다 — 붙여 넣은 토큰은 외부로 전송되지 않습니다 — 그래서 평소 같으면 임의의 웹 폼에 붙여 넣기 망설여질 운영 토큰도 안심하고 사용할 수 있습니다.