디코딩된 JWT가 증명하는 것
JWT 디코더는 토큰이 주장하는 내용을 보여줍니다. 그러한 주장이 사실인지 여부를 보여줄 수는 없습니다. 이 가이드에서는 세 가지 세그먼트가 무엇인지, 디코딩이 수행하는 것과 설정하지 않는 것, 둘 사이의 격차에 존재하는 공격을 다룹니다.
3개의 세그먼트(그 중 2개는 JSON임)
일반적인 형식의 JWT는 점으로 구분된 3개의 base64url 세그먼트인 JWS입니다. 첫 번째는 헤더, 두 번째는 페이로드, 세 번째는 서명입니다.
헤더와 페이로드는 base64url로 인코딩된 일반 JSON 개체입니다. 암호화되지 않고 인코딩되었습니다. 토큰을 보유한 사람은 누구나 키 없이 즉시 두 가지를 모두 읽을 수 있습니다. 이는 결함이 아니라 설계입니다. JWT는 봉인된 봉투가 아닌 서명된 명세서입니다. 서명은 해당 진술이 변경되지 않았음을 보장합니다. 비공개로 유지하는 데는 아무런 영향을 미치지 않습니다.
결과는 일상적으로 놓치기 때문에 명확하게 언급할 가치가 있습니다. JWT 페이로드에 기밀 내용을 넣지 마십시오. 비밀번호나 전체 국가 식별자, 내부 시스템 세부정보가 아닙니다. 토큰을 가지고 있는 사람은 누구나 페이로드를 공개할 수 있으므로 페이로드가 공개라고 가정합니다.
세 번째 세그먼트는 처음 두 세그먼트에 대해 계산된 서명입니다. 보안 가치를 지닌 유일한 부분이며, 디코더가 평가할 수 없는 부분입니다.
디코딩이 증명하는 것: 아무것도 없음
이것이 전체 가이드의 요점입니다. JWT를 디코딩하면 두 개의 base64url 문자열이 JSON으로 구문 분석됩니다. 토큰이 올바른 형식으로 구성되어 있음을 확인합니다. 토큰이 진짜인지, "iss" 클레임에 이름이 지정된 당사자가 발행했는지, 클레임이 편집되지 않았는지, 토큰이 유효한지 여부는 확인되지 않습니다.
누구나 토큰을 생성할 수 있습니다. JWT을 선택하고 "role": "user"를 "role": "admin"으로 변경하고 페이로드를 다시 인코딩하고 끝에 서명을 스테이플하면 디코더가 편집된 주장을 원본에 표시된 것과 똑같이 정확하게 표시합니다. 차이를 확인하는 것은 디코더에 없는 키가 필요한 다른 작업이기 때문에 차이를 알 수 있는 방법이 없습니다.
따라서 디코더(이것이든 다른 것이든)가 "exp: 2026-01-01"를 표시할 때 실제로 말하는 것은 이 토큰에 해당 날짜에 만료된다는 주장이 포함되어 있다는 것입니다. 해당 주장이 무엇을 의미하는지 여부는 전적으로 서명이 유효한지 여부에 달려 있으며 이는 확인되지 않았습니다.
이 도구는 디코딩만 하고 페이지의 결과 옆에 매번 그렇게 표시합니다. 각주에는 없습니다. 그 이유는 이에 대해 침묵을 지키는 디코더가 사용자에게 검증되지 않은 데이터를 마치 검증된 것처럼 읽도록 교육하고 있으며, 이러한 습관이 전체 인증 버그 계열의 뿌리이기 때문입니다.
이 도구가 확인 기능을 제공하지 않는 이유
확인에는 웹페이지가 책임감 있게 가질 수 없는 세 가지 요소, 즉 발급자의 키, 미리 고정된 알고리즘, 거부할 항목에 대한 정책이 필요합니다.
핵심은 명백한 문제이다. HMAC 알고리즘(HS256 및 기타)의 경우 키는 공유 비밀입니다. 이는 토큰을 생성하는 데 사용되는 것과 동일한 비밀입니다. 웹페이지에 붙여넣는다는 것은 유효한 토큰을 발행할 수 있는 자격 증명을 웹페이지에 붙여넣는 것을 의미합니다. RSA 및 ECDSA의 경우 공개 키는 비밀이 아니지만 올바른 JWKS 엔드포인트와 신뢰에서 올바른 키를 가져와야 합니다.
알고리즘은 미묘한 문제이며 잘 알려진 두 가지 공격의 소스입니다. 첫 번째는 alg입니다: "none": 헤더는 토큰이 서명되지 않았다고 주장하며 자체 구성이 아닌 헤더를 존중하는 검증자는 무엇이든 허용합니다. 두 번째는 RS256-HS256 혼동입니다. 공격자는 공개 키(정의상 공개)를 가져와 헤더를 HS256으로 변경하고 해당 공개 키를 HMAC 비밀로 사용하여 토큰에 서명합니다. 토큰에서 알고리즘을 읽고 "키"를 찾는 검증자가 이를 검증합니다.
두 공격 모두 동일한 실수에서 비롯됩니다. 즉, 토큰이 검증자에게 토큰 확인 방법을 알려주도록 하는 것입니다. 올바른 검증자는 헤더의 알고리즘을 무시하고 구성된 알고리즘을 사용합니다. 이는 편리한 도구나 양식에 무언가를 붙여넣은 사람이 아닌 토큰을 신뢰하는 시스템에 속하는 결정입니다.
생산 토큰을 어디에든 붙여넣으면 안 되는 이유
액세스 토큰은 무기명 자격 증명입니다. 이것이 Authorization 헤더에서 "bearer"가 의미하는 바입니다. 이를 보유하는 사람은 바로 귀하입니다. 두 번째 요소는 없으며 일반적으로 도난당한 토큰과 합법적인 토큰을 구분할 수 있는 방법이 없습니다. 만료될 때까지 이는 귀하의 계정에서 작동하는 키입니다.
따라서 웹 페이지에 라이브 토큰을 붙여넣는 것은 해당 페이지에 자격 증명을 전달하는 것입니다. 이것은 모든 것을 로컬에서 디코딩하고 페이지가 로드된 후 네트워크 요청을 하지 않습니다. 브라우저의 네트워크 패널에서 이를 확인할 수 있으며, 10초가 걸리기 때문에 확인해야 합니다. 그러나 그 주장이 실제로 무엇인지 주목하십시오. 웹사이트에서 웹사이트를 신뢰할 수 있다는 주장입니다. 토큰을 유출하는 모든 사이트는 정확히 동일한 주장을 하며 방문자는 한눈에 차이점을 알 수 없습니다.
안전한 습관은 사이트를 올바르게 판단하는 데 달려 있지 않습니다. 만료된 토큰, 테스트 환경 토큰 또는 해당 목적으로 발행한 토큰을 사용하세요. 이미 생산 토큰을 어딘가에 붙여넣었다면 회전하세요. 취소 비용은 저렴합니다. 사건은 아닙니다.
서명 키에 더 많은 힘을 가하는 경우에도 마찬가지입니다. 웹 페이지에 HMAC 비밀 키나 개인 키를 입력할 정당한 이유가 없으며 토큰을 "verify"하는 것을 요구하는 사이트는 토큰 위조 기능을 요구하는 것입니다. 이것이 이 도구에 확인 기능이 없는 구체적인 이유입니다. 이 기능에는 요청이 필요합니다.
중요한 주장 읽기
RFC 7519는 작은 클레임 이름 집합을 등록합니다. "iss"는 발행자, "sub"는 토큰에 관한 주제, "aud"는 의도된 청중, "exp"는 만료, "nbf"는 가장 빠른 유효 시간, "iat"는 발행 시간, "jti"는 재생 감지를 위한 고유 ID입니다. 다른 모든 것은 응용 프로그램에 따라 다릅니다.
시간 주장은 NumericDate 값입니다. 즉, 밀리초가 아니라 Unix 시대 이후의 초입니다. 대부분의 JavaScript 시간 값은 밀리초이기 때문에 이로 인해 사람들이 끊임없이 당황하게 됩니다. 1970에 만료되는 것으로 보이는 토큰에는 일반적으로 밀리초 값이 지정됩니다. 55000년에 만료되는 것으로 보이는 값은 일반적으로 어딘가에서 1000를 곱한 두 번째 값을 가집니다.
"aud"는 디버깅할 때 특별한 주의를 기울여야 합니다. 완전히 유효한 토큰이라도 다른 대상을 위해 발행되었기 때문에 여전히 잘못된 토큰일 수 있습니다. 서명을 확인하지만 청중은 확인하지 않는 검증자는 완전히 다른 서비스를 위해 발행된 토큰을 수락합니다. 이는 ID 공급자를 공유하는 시스템에서 실제 권한 상승 경로입니다.
이 도구는 시간 청구를 UTC로 렌더링하고, 만료된 토큰을 만료된 것으로 표시하고, 서명이 유효한 경우에만 만료 청구가 의미가 있다는 알림과 함께 표시합니다. "만료되지 않았다고 표시됩니다"는 확인되지 않은 데이터 습관이 손상을 입히는 정확한 순간이기 때문에 알림이 존재합니다.
신뢰를 수행하는 시스템에 대한 간단한 체크리스트
단순히 토큰을 검사하는 것이 아니라 토큰을 허용하는 코드를 작성하는 경우 올바른 검증자가 수행하는 작업에 대한 간략한 버전은 다음과 같습니다.
- 청구서를 읽기 전에 대역 외부에서 얻은 키를 사용하여 먼저 서명을 확인하십시오.
- 자신의 구성에 알고리즘을 고정하십시오. 토큰 헤더에서 읽지 마십시오. "none"을 무조건 거부합니다.
- 신뢰할 수 있는 시계와 비교하여 "exp" 및 "nbf"를 확인하세요. 왜곡에 대한 허용 오차는 최대한 작습니다.
- 예상한 값과 비교하여 "iss" 및 "aud"를 확인하세요. 다른 사람을 위한 토큰의 유효한 서명은 여전히 잘못된 토큰입니다.
- 직접 조립하기보다는 플랫폼에 맞게 검증된 라이브러리를 사용하세요. 이 목록의 모든 항목은 구현이 잘못되었기 때문에 여기에 있습니다.
- 토큰 수명을 짧게 유지하고 해지 경로를 확보하세요. 수명이 짧은 토큰은 아직 알아차리지 못한 누출로 인한 피해를 제한합니다.
붙여넣은 내용은 어떻게 되나요?
- 모든 변환, 해시, 디코드 및 차이점은 브라우저 탭에서 실행됩니다. 페이지가 로드된 후에는 관련된 서버가 없기 때문에 입력이 서버에 업로드되거나 기록되거나 저장되지 않습니다.
- 해시는 브라우저의 자체 웹 암호화 구현에서 나오고 UUID는 암호화된 보안 무작위 생성기에서 나옵니다. 둘 다 네트워크 호출과 관련이 없습니다.
- 귀하가 입력하는 내용은 로컬 저장소나 쿠키에 기록되지 않습니다. 페이지를 다시 로드하면 페이지가 삭제됩니다. 탭을 닫으면 삭제됩니다.
- 사이트 전체 분석은 구성된 정식 프로덕션 호스트에서만 실행되며 개인정보 보호정책에 공개됩니다. 로컬 및 미리보기 호스트는 이를 거부합니다. 붙여넣은 값, 토큰, URL 및 파일 내용은 ToolAcre 자체 분석 이벤트에서 제외됩니다. 현재 구성에서는 광고가 비활성화되어 있습니다.
- 즉, JWT 또는 API 키는 실시간 자격 증명입니다. 안전한 습관은 자신이 작성하지 않은 웹 페이지에 내용을 붙여넣지 않는 것입니다. 이 내용을 포함하여 그 주장은 신뢰할 만합니다.
질문
이 도구는 서명을 확인합니까?
아니요, 절대 그렇지 않습니다. 헤더와 페이로드를 디코딩하고 여기에 포함된 내용을 보여줍니다. 서명을 확인하지 않으므로 표시되는 어떤 것도 토큰이 정품인지, 변경되지 않았는지, 이름이 지정된 사람이 발행했는지 증명할 수 없습니다.
그렇다면 토큰이 진짜인지 어떻게 알 수 있나요?
검증된 라이브러리를 사용하여 발급자의 키로 서명을 확인하고, 알고리즘을 토큰에서 읽는 대신 자체 구성에 고정합니다. 이는 키를 합법적으로 보유하고 있는 환경에서 토큰을 신뢰하는 서비스에 대한 작업입니다.
여기에서 토큰을 디코딩하면 내 토큰이 어디로 보내지나요?
아니요. 디코딩은 페이지의 자체 JavaScript를 사용하여 브라우저 탭에서 이루어지며 페이지는 로드된 후 네트워크 요청을 하지 않습니다. 브라우저의 네트워크 패널에서 이를 확인할 수 있습니다. 습관적으로 생산 토큰을 웹 도구에 붙여넣어서는 안 됩니다. 왜냐하면 그 습관은 정직하지 않은 사이트에서 작동해야 하기 때문입니다.
왜 누구나 내 JWT 페이로드를 읽을 수 있나요?
페이로드는 암호화되지 않고 base64url로 인코딩되기 때문입니다. JWS는 봉인된 명세서가 아니라 서명된 명세서입니다. 콘텐츠를 읽을 수 없도록 해야 하는 경우 암호화된 토큰 형식인 JWE가 필요합니다. 그러면 디코더는 키 없이는 아무것도 표시할 수 없습니다.
alg: "none"란 무엇인가요?
토큰이 서명되지 않았음을 선언하는 헤더 값입니다. 이는 다른 수단으로 무결성이 보장되는 컨텍스트에 대한 사양에 존재하며 이는 고정 함정입니다. 헤더의 알고리즘을 신뢰하는 검증자는 "none"를 주장하는 모든 토큰을 허용합니다. 이 도구는 나타날 때마다 플래그를 지정합니다.
내 토큰에는 5개의 세그먼트가 있으며 디코딩되지 않습니다. 왜?
5개의 세그먼트는 서명된 JWS가 아닌 암호화된 토큰인 JWE를 의미합니다. 암호 해독 키 없이는 내용을 읽을 수 없으므로 디코더가 실제로 표시할 내용은 없습니다. 이 도구는 모호한 구문 분석 실패를 보고하는 대신 해당 사례를 명시적으로 식별합니다.
만료가 1000만큼 잘못된 것 같습니다.
JWT 시간 청구는 NumericDate: 밀리초가 아닌 epoch 이후의 초입니다. Date.now()에 의해 생성된 값은 1000배 너무 큽니다. 이 툴킷의 타임스탬프 유틸리티는 둘 사이를 변환하고 항상 어떤 단위를 사용했는지 알려줍니다.
localStorage에 JWT를 저장하는 것이 안전합니까?
이는 예 또는 아니오가 아닌 절충안입니다. localStorage는 원본에서 실행되는 모든 JavaScript에서 읽을 수 있으므로 단일 XSS 취약점으로 인해 토큰이 유출됩니다. httpOnly 쿠키는 JavaScript로 읽을 수 없지만 CSRF 보호가 필요합니다. 솔직하게 요약하면 둘 다 무료가 아니며 결정은 애플리케이션의 위협 모델에 따라 결정된다는 것입니다.
제한사항
- 이 도구는 디코딩만 합니다. 이는 서명을 확인하지 않으며 이는 누락된 기능이 아니라 영구적인 디자인 결정입니다. 이유는 위 가이드를 참조하세요.
- 암호화된 토큰(JWE, 5개 세그먼트)은 키 없이는 전혀 디코딩할 수 없습니다. 도구는 이를 식별하고 중지합니다.
- 페이로드 자체가 토큰인 중첩된 JWT는 자동으로 래핑 해제되지 않습니다. 내부 토큰을 별도의 단계로 디코딩합니다.
- RFC 7519에 정의된 등록된 집합 이상의 클레임 의미는 애플리케이션별로 다르므로 도구에서는 해당 값을 해석하지 않고 표시합니다.
- 여기에 표시된 만료는 토큰이 자체적으로 주장하는 내용만 반영합니다. 해당 주장이 의미가 있는지 여부는 이 도구가 확인하지 않는 서명에 따라 다릅니다.
- 200,000 문자보다 큰 토큰은 거부됩니다. 실제 JWT는 크기가 몇 배 더 작습니다.