본문으로 건너뛰기
ZEKILO Dev
인코딩

JWT 디코딩과 서명 검증의 차이 — exp·iat 읽기

2026.10.03 업데이트 · 4분 읽기

JWT 디코딩은 Base64URL을 풀어 읽는 것일 뿐 서명 검증이 아닙니다. 헤더·페이로드 구조, exp·iat·nbf 시각을 읽는 법, alg: none 토큰을 받으면 안 되는 이유를 RFC 7519 기준으로 정리했습니다.

JWT 디코더 바로 사용하기

디코딩은 서명 검증이 아닙니다. JWT 내용은 누구나 읽을 수 있으니 비밀 정보를 넣지 마세요. JWT 디코더에 토큰을 붙여 넣으면 헤더와 페이로드, 만료 시각을 볼 수 있지만, 그것은 읽기일 뿐입니다. 그 토큰을 믿어도 되는지는 받는 서버가 키로 서명을 검증하고 exp·aud 같은 클레임을 확인해야 정해집니다.

JWT는 세 부분이다

서명된 JWT는 헤더.페이로드.서명 꼴이고, 세 부분은 각각 패딩 없는 Base64URL로 적혀 있습니다(RFC 7515). 예시 토큰을 보겠습니다. HS256 알고리즘과 예시용 비밀 키 zekilo-secret으로 서명한 것입니다.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlpFS0lMTyIsImlhdCI6MTc2NzIyNTYwMCwiZXhwIjoxNzY3MjI5MjAwfQ.218DR4r7DRNkKO3XJcQYXE0ztwbqxYMeQpkIp9S18oE
부분 디코딩한 내용
헤더 {"alg":"HS256","typ":"JWT"}
페이로드 {"sub":"1234567890","name":"ZEKILO","iat":1767225600,"exp":1767229200}
서명 32바이트 HMAC-SHA256 값(Base64URL 43자)

헤더와 페이로드는 암호화된 것이 아니라 Base64URL로 적힌 JSON입니다. 키 없이도 읽을 수 있습니다. 내용까지 암호화한 JWT는 JWE라는 별도 형식이며 점으로 나눈 부분이 다섯 개입니다.

디코딩과 검증은 무엇이 다른가

항목 디코딩 서명 검증
하는 일 Base64URL을 풀어 JSON 읽기 키로 서명을 확인
필요한 것 없음 비밀 키(HS256) 또는 공개 키(RS256·ES256)
알 수 있는 것 토큰에 적힌 내용 그 내용이 키를 가진 쪽이 서명한 그대로인지
할 수 있는 쪽 누구나 키를 가진 쪽

서명 대상은 BASE64URL(헤더), 점, BASE64URL(페이로드)를 이은 문자열입니다(RFC 7515). 위 토큰의 앞 두 부분에 대해 zekilo-secret으로 HMAC-SHA256을 계산하면 세 번째 부분과 같은 값이 나옵니다. 페이로드가 한 글자라도 달라지면 계산 결과가 달라지므로, 검증하는 쪽은 내용이 바뀌었다는 것을 알 수 있습니다.

검증은 서명 확인으로 끝나지 않습니다. 받는 쪽은 만료 시각(exp)과 발급자(iss), 대상(aud)도 확인해야 합니다. RFC 7519는 aud 클레임이 있는데 그 값에 자신이 없으면 토큰을 거부해야 한다고 정합니다.

exp·iat·nbf 읽기

세 클레임의 값은 NumericDate, 곧 1970-01-01T00:00:00Z부터 센 초입니다(RFC 7519 2절). 밀리초가 아닙니다. JavaScript의 Date.now()는 밀리초를 돌려주므로 1000으로 나눈 뒤 비교합니다.

클레임 뜻 예시 토큰의 값 날짜
iat 발급한 시각 1767225600 2026-01-01 00:00:00 UTC (KST 09:00)
exp 이 시각부터는 받으면 안 됨 1767229200 2026-01-01 01:00:00 UTC (KST 10:00)
nbf 이 시각 전에는 받으면 안 됨 없음 —

exp에서 iat를 빼면 3,600초이므로 이 토큰은 발급 후 1시간 동안 유효합니다. RFC 7519는 서버 사이의 시계 차이를 감안해 보통 몇 분을 넘지 않는 여유를 둘 수 있다고 합니다. 등록 클레임은 모두 선택 사항이라 토큰에 없을 수도 있습니다.

숫자를 날짜로 바꿀 때는 Unix 타임스탬프 변환에 1767225600을 넣으면 2026-01-01T00:00:00Z, 한국 시간 2026-01-01 09:00:00으로 보여 줍니다. JWT 디코더도 exp·nbf·iat 옆에 날짜를 표시하고, exp가 지났으면 만료 경고를 보여 줍니다. 이 표시는 브라우저 시계와 비교한 참고 정보이며 검증을 대신하지 않습니다.

alg: none과 알고리즘 확인

RFC 7519 6절은 서명이 없는 JWT(Unsecured JWT)를 정의합니다. 헤더의 alg가 none이고 서명 부분이 비어 있습니다. 다른 수단으로 보호되는 경우를 위한 형식이지, 일반 인증 토큰으로 받을 수 있는 것이 아닙니다.

  • RFC 7518 3.6절: 구현은 서명 없는 토큰을 기본으로 받아들여서는 안 됩니다.
  • RFC 8725 3.1절: 라이브러리는 호출하는 쪽이 허용할 알고리즘 목록을 지정할 수 있게 해야 하고, 그 밖의 알고리즘을 써서는 안 됩니다.

따라서 서버는 허용할 알고리즘을 코드에 정해 두고, 토큰 헤더에 적힌 alg를 그대로 따르지 않아야 합니다. JWT 디코더는 alg가 none인 토큰에 서명이 없다는 경고를 보여 줍니다.

실무에서 지킬 것

  • 페이로드에 비밀번호, 주민등록번호, 카드 번호 같은 정보를 넣지 않습니다.
  • 토큰은 가진 사람이 쓸 수 있는 출입증입니다. 운영 환경의 토큰을 채팅, 이슈, 로그에 붙이지 않습니다.
  • 검증은 서버에서 검증된 라이브러리로 합니다. 알고리즘을 정해 두고, 서명과 exp·nbf·iss·aud를 확인합니다.
  • 클라이언트에서 디코딩한 값은 화면 표시용으로만 씁니다. 권한 판단은 서버가 검증한 결과로 합니다.

이 글과 관련된 도구

출처와 기준

다른 가이드