JWT 디코더
JWT(JSON Web Token)를 브라우저에서 디코딩하여 헤더, 페이로드, 서명을 시각화합니다.iat / exp / nbf 는 사람이 읽을 수 있는 날짜로 자동 변환됩니다.
Header
Payload
Signature
* 서명 검증은 비밀 키 또는 공개 키가 필요하므로 이 도구는 디코딩만 수행합니다.
JWT 구조에 대해
JWT는 header.payload.signature 세 부분을 점(.)으로 연결한 문자열입니다. 헤더와 페이로드는 Base64URL로 인코딩된 JSON이고, 서명은 헤더에 지정된 알고리즘(HS256 / RS256 등)으로 생성됩니다.
주요 클레임: iss(발급자), sub(주체), aud(수신자), exp(만료), nbf(유효 시작), iat(발급 시각), jti(JWT ID)
📖 자주 걸리는 지점
JWT 의 헤더 · 페이로드 · 서명을 분해해 표시하고 exp 나 iat 같은 시각 클레임을 일시로 변환합니다. 처리는 브라우저 안에서 끝나며 서명의 검증은 하지 않습니다(키가 필요하므로). 여기서 짚어야 할 것은 디코딩할 수 있다는 것은 내용을 누구나 읽을 수 있다는 것이라는 점입니다 — JWT 의 페이로드는 암호화되어 있지 않고 단지 Base64URL 로 부호화되어 있을 뿐입니다.
| 사례 | 무슨 일이 일어나는가 | 어떻게 하면 되는가 |
|---|---|---|
| 페이로드에 기밀을 넣어 버린다 | Base64URL 은 부호화이지 암호화가 아닙니다 — 이 페이지가 그러하듯 키를 갖지 않은 누구나 내용을 읽을 수 있습니다. 그런데도 서명되어 있으니 안전하다는 오해는 뿌리 깊어 페이로드에 개인정보 · 내부의 권한 구조 · 사내 ID, 때로는 메일 주소와 생년월일을 한꺼번에 넣어 버리는 설계가 보입니다. JWT 는 브라우저의 localStorage 나 쿠키에 저장되고 로그에도 남고 URL 에 실리는 경우도 있습니다 — 내용은 공개 정보로 다뤄진다고 생각하는 것이 정확합니다. |
페이로드에는 식별자만 넣고 내용은 서버에서 조회하세요 — 사용자 ID 와 역할 이름 정도로 그치고 성명 · 메일 주소 · 주소는 넣지 않습니다. 암호화가 필요하다면 JWE(JSON Web Encryption)라는 별도의 사양이 있습니다만 그 전에 애초에 JWT 에 넣을 필요가 있는지를 다시 물으세요 — 많은 경우 필요한 것은 누구인가뿐이고 무엇인가는 서버가 알고 있으면 충분합니다. 판단의 기준은 단순해서 그 값이 액세스 로그에 평문으로 나와도 괜찮은가 입니다 — 괜찮다면 넣어도 되고 곤란하다면 넣어서는 안 됩니다. |
| 디코딩했을 뿐인데 내용을 믿어 버린다 | 서명을 검증하지 않으면 JWT 는 사용자가 자유롭게 고쳐 쓸 수 있는 문자열에 지나지 않습니다. 디코딩해 role: "admin" 을 읽고 그대로 권한 판정에 쓰는 코드는 공격자가 스스로 그 값을 쓴 토큰을 그대로 받아들입니다. 역사적으로 유명한 실패가 두 가지 있으며 하나는 alg: "none" 공격 — 헤더의 알고리즘을 none 으로 고쳐 쓰고 서명을 비우면 그것을 순순히 받아들이는 구현이 있었기 때문입니다. 또 하나는 HS256 과 RS256 의 혼동으로 공개 키를 HMAC 의 공유 키로 쓰게 함으로써 공개 정보만으로 유효한 서명을 만들 수 있게 됩니다. |
검증은 반드시 라이브러리의 검증 함수로 하고 허용할 알고리즘을 명시적으로 고정하세요 — jwt.verify(token, key, { algorithms: ["RS256"] }) 처럼 씁니다. 이 인수를 생략하면 헤더의 alg 를 믿는 구현이 있습니다 — 공격자가 고쳐 쓸 수 있는 값을 검증 방식의 결정에 써서는 안 됩니다. 디코딩만 하는 함수(jwt.decode 같은 이름의 것)를 인가의 판단에 쓰지 마세요 — 이름이 비슷해 혼동되지만 이것은 검증을 하지 않습니다. 용도는 디버그 표시뿐입니다. 구현을 쓰기 전에 그 라이브러리의 검증하는 함수와 디코딩만 하는 함수를 반드시 확인하세요. |
| 로그아웃했는데 토큰이 쓸 수 있다 | JWT 는 스테이트리스인 것이 설계상의 이점이자 동시에 최대의 제약입니다. 서버는 토큰을 저장하고 있지 않으므로 발행한 토큰을 개별적으로 무효화할 수단이 없습니다 — 로그아웃이란 실제로는 클라이언트 쪽에서 토큰을 버리는 것에 지나지 않으며 그 토큰을 복사해 갖고 있던 자는 유효 기간까지 계속 쓸 수 있습니다. 같은 이유로 권한을 박탈해도 이미 발행된 토큰에는 옛 권한이 쓰인 채입니다. 퇴직자의 접근을 막았을 텐데 한 시간 쓸 수 있었다는 것이 이것입니다. | 유효 기간을 짧게 하고 리프레시 토큰과 조합하세요 — 액세스 토큰은 15 분 정도로 하고 갱신에는 서버 쪽에서 관리하는 리프레시 토큰을 씁니다. 이 구성이라면 리프레시 토큰을 무효화함으로써 최대 15 분이면 권한이 끊깁니다. 즉시 실효가 요건이라면 블록리스트(실효시킨 토큰의 ID 를 저장하고 검증 시에 대조)가 필요합니다 — 다만 이는 스테이트를 갖는다는 뜻이므로 JWT 를 쓰는 이점의 일부를 내려놓게 됩니다. 판단은 여기가 분기점으로 — 즉시 실효가 정말로 필요하다면 순순히 세션 ID 와 서버사이드 세션을 쓰는 편이 단순하고 확실합니다. JWT 가 맞는 것은 복수의 서비스를 넘나드는 인증이나 검증만을 분산시키고 싶은 경우입니다. |
시각 클레임은 초입니다. exp · iat · nbf 는 모두 Unix 초이며 밀리초를 넣으면 유효 기간이 5 만 년 뒤가 되어 실질적으로 무기한인 토큰이 만들어집니다 — JavaScript 의 Date.now() 가 밀리초를 반환하므로 Date.now() + 3600000 처럼 써서 그대로 exp 에 넣는 실수는 대단히 자주 일어납니다(올바르게는 Math.floor(Date.now() / 1000) + 3600). 디코딩 결과의 일시가 먼 미래가 되어 있다면 이 실수를 의심하세요 — 이 페이지가 시각을 일시로 변환해 표시하는 것은 바로 그것을 눈으로 확인하기 위해서입니다. 한 가지 더, 서버 간 시계의 어긋남에 대비해 nbf 와 exp 의 검증에는 수십 초의 허용(leeway)을 두세요 — 주요 라이브러리에는 그 옵션이 있습니다. 마지막으로 운용상의 주의로 유효 기간 내의 프로덕션 토큰을 외부의 온라인 JWT 디코더에 붙이지 마세요 — 그것은 인증 정보 그 자체이며 붙인 곳의 서버 로그에 남으면 그대로 남이 사칭할 수 있습니다. 이 페이지는 브라우저 안에서 끝나지만 그 보장은 개발자 도구의 Network 탭에서 확인하는 습관을 가지세요.
📖 사용법
-
1
JWT 복사API 응답이나 Authorization 헤더에서 JWT 문자열을 가져옵니다.
-
2
입력란에 붙여넣기JWT를 상단 텍스트 영역에 붙여넣으면 자동으로 디코딩됩니다.
-
3
헤더 · 페이로드 확인알고리즘(alg), 발급자(iss), 만료(exp) 등의 클레임을 시각적으로 확인합니다.
❓ 자주 묻는 질문
JWT란?
서명 검증도 가능한가요?
입력한 JWT가 서버로 전송되나요?
exp가 만료된 경우는?
🐛 이 도구에서 문제가 발생했나요?
무료 · 가입 불필요. 재현 절차만이라도 도움이 됩니다. 보고는 운영자에게 직접 전달되어 개선에 사용됩니다.
보고 감사합니다!
운영자에게 전달되었습니다. 개선에 사용됩니다.