JWS 디코딩이 시작점에 불과한 이유
최신 ID 아키텍처, 특히 OAuth 2.0 및 OpenID Connect에서 서명된 토큰은 서비스 간에 전달할 수 있는 '신뢰할 수 있는 컨테이너'로 취급되는 경우가 많습니다. 하지만 보안 엔지니어, 레드팀원, AI 보안 연구원에게 JWS는 암호화와 비즈니스 로직의 교차점에 있는 공격자가 제어하는 간결한 입력으로 더 잘 이해됩니다. 원시 JSON 웹 서명 디코딩 가 목표가 아니라 검증자가 무엇을 추론할 수 있게 해주는 도구입니다. 생각 애플리케이션의 유효성을 검사하고 있습니다. 실제로 가 인증을 위해 의존하는 부분과 토큰의 어느 부분이 신뢰할 수 없는 데이터가 아닌 구성으로 취급되고 있는지 확인합니다.
이 구분이 중요한 이유는 '토큰 보안'은 토큰 형식의 속성이 아니기 때문입니다. 이는 토큰 형식의 속성이 아니라 검증 및 클레임 유효성 검사 파이프라인-알고리즘 선택, 키 선택, 발급자/청중 확인, 시간 기반 클레임, 그리고 중요한 것은 다운스트림 코드가 클레임을 확보한 후 이를 사용하는 방식입니다. 검증이 완료되거나 실행되기 전에 시스템이 디코딩된 클레임을 '진실'로 취급하는 순간, 토큰은 보안 경계가 아닌 사용자가 제어하는 파라미터가 됩니다.

문자열 뒤에 숨겨진 엔지니어링: 패딩 없는 Base64url
사람들이 "JWS를 디코딩한다"고 할 때 일반적으로 하는 일은 base64url 인코딩 단계를 거꾸로 하는 것입니다. JWS 컴팩트 직렬화에서 세 세그먼트 각각은 URL 안전 알파벳을 사용하여 base64url로 인코딩되며 일반적으로 다음을 생략합니다. = 패딩. RFC 7515는 압축 형식을 정의하고 명시적으로 다음을 사용합니다. BASE64URL(...) 를 헤더 및 페이로드 구성에 사용한 다음, 다음과 같이 연결합니다. . 구분 기호. (RFC 편집기)
'패딩 없음' 규칙은 툴을 구축하거나 캡처된 트래픽을 대규모로 분석하기 전까지는 사소하게 들릴 수 있습니다. 많은 일반적인 base64 루틴은 패딩이 있다고 가정하고 패딩이 누락되면 오류를 발생시키거나 모호한 출력을 생성합니다. 강력한 디코더는 패딩을 결정적으로 다시 추가하고 base64url 인식 디코딩 루틴을 사용해야 하며, 그렇지 않으면 특히 퍼징 중에 기형이거나 의도적으로 손상된 토큰을 처리하는 경우 분석을 재현할 수 없게 됩니다.
JWS와 JWT: 각 점이 실제로 의미하는 것
JWS 컴팩트 토큰은 항상 있습니다:
BASE64URL(헤더) . BASE64URL(페이로드) . BASE64URL(서명)
세 번째 세그먼트는 "16진수"가 아닙니다. 서명(또는 MAC) 바이트의 base64url입니다. 확인하거나 암호화 분류를 수행하지 않는 한 불투명하게 처리하세요.
JWT(RFC 7519)는 일반적으로 JWS 페이로드 내에서 전달되는 클레임 세트 규칙이므로 사람들이 "JWT"와 "JWS"를 자연스럽게 혼동하는 경우가 있습니다. RFC 7519는 다음과 같이 등록된 클레임을 정의합니다. iss, sub, aud, exp및 nbf를 사용하여 이러한 클레임이 JSON 객체로 표현되는 방식을 설명합니다. (IETF 데이터트래커)
실제로, 이는 디코딩 단계만 거치면 토큰이 무엇인지 알 수 있음을 의미합니다. 클레임하지만 이러한 주장이 true. 서명 확인과 클레임 유효성 검사에 성공해야만 진실이 드러납니다.

헤더는 공격 표면입니다: alg, kid, jku 신뢰할 수 없는 입력으로
잘 설계된 시스템에서 토큰 헤더는 검증자가 알려진 좋은 검증 전략을 선택하는 데 도움이 되는 메타데이터입니다. 잘못 설계된 시스템에서는 헤더가 공격자가 제어하는 구성 채널이 됩니다. 그렇기 때문에 JSON 웹 서명 디코딩를 사용하면 많은 전문가가 헤더에 먼저 집중합니다.
그리고 alg 값은 호출되는 확인 방법에 영향을 줄 수 있기 때문에 특히 민감합니다. 알고리즘 혼동(키 혼동이라고도 함)은 공격자가 서버가 개발자가 의도한 것과 다른 알고리즘을 사용하여 JWT를 확인하도록 하여 서버의 서명 키를 몰라도 위조된 토큰을 사용할 수 있게 할 때 발생합니다. 포트스위거의 웹 보안 아카데미에서는 이 클래스에 대해 명확하게 설명하며 알고리즘 선택의 잘못된 구성 또는 결함 처리와 연관시킵니다. (포트스위거)
그리고 kid 매개변수는 암호화가 아니라 키 검색입니다. 만약 kid 가 엄격한 허용 목록 없이 파일 경로, 데이터베이스 쿼리, 캐시 키 또는 동적 리졸버에 영향을 미치는 경우 키 관리 경계 내에서 일반적인 인젝션 표면이 됩니다.
그리고 jku 매개변수는 오용될 경우 훨씬 더 위험합니다. 서버가 토큰 헤더에 지정된 URL(또는 충분히 제한되지 않은 변형)에서 JWKS를 가져오는 경우 공격자는 검증기를 공격자가 제어하는 키를 가리켜 신뢰 앵커를 교체하려고 시도할 수 있습니다. 시스템이 "HTTPS에서만 가져오는" 경우에도 엄격한 허용 목록, 발급자 바인딩, 캐싱 정책 및 감사 추적이 없으면 키 검색은 암호화 문제가 아닌 공급망 문제로 바뀝니다.
파이썬으로 제작된 프로덕션급 오프라인 디코더(디코딩 전용, 패딩 안전)
웹 기반 토큰 디버거는 편리하지만 전문적인 보안 작업에는 적합하지 않은 경우가 많습니다. 토큰에는 민감한 식별자, 이메일, 내부 테넌트 ID 또는 임베디드 PII가 포함될 수 있습니다. 따라서 잘못된 입력에 대해 결정적이고 안전한 오프라인의 스크립트 가능한 도구가 필요합니다. 아래 구현은 의도적으로 아무것도 '검증'하지 않고, 존재하는 것을 디코딩하고 실패 모드를 관찰할 수 있게 할 뿐입니다.
base64 가져오기
import json
import Any, Dict, Tuple, Optional을 입력합니다.
def _b64url_decode(data: str) -> 바이트:
# RFC 7515 base64url은 일반적으로 "=" 패딩을 생략합니다. 결정론적으로 복원합니다.
pad = (-len(data)) % 4
return base64.urlsafe_b64decode((data + "=" * pad).encode("ascii"))
def _parse_json(b: 바이트) -> Tuple[Optional[Dict[str, Any]], Optional[str]]:
try:
return json.loads(b.decode("utf-8")), None
예외는 e로 제외합니다:
반환 None, f"{type(e).__name__}: {e}"
def jws_decode(token: str) -> Dict[str, Any]:
parts = token.split(".")
if len(parts) != 3:
반환 {"오류": "잘못된 JWS 압축 형식입니다(3개 부분으로 예상).", "parts": len(parts)}
header_b = _b64url_decode(parts[0])
payload_b = _b64url_decode(parts[1])
header, he = _parse_json(header_b)
payload, pe = _parse_json(payload_b)
out: Dict[str, Any] = {
"header_raw": header_b.decode("utf-8", errors="replace"),
"payload_raw": payload_b.decode("utf-8", errors="replace"),
"signature_b64url_sample": parts[2][:16] + "..."
}
header가 None이 아닌 경우:
out["header"] = header
else:
out["header_error"] = he
페이로드가 None이 아닌 경우
out["payload"] = payload
else:
out["payload_error"] = pe
반환 아웃
이는 RFC 7515의 base64url 사용(그리고 컴팩트 JWS가 URL/HTTP 헤더 컨텍스트용으로 설계된 현실)에 부합하는 동시에 토큰이 변형되거나 잘리거나 의도적으로 퍼지더라도 안정적인 동작을 제공합니다.RFC 편집기)
중요한 차이점: 디코딩과 검증(그리고 실제 버그 패턴)
JWS/JWT와 관련하여 가장 고질적인 보안 오류는 디코딩과 검증을 혼동하는 것입니다. 디코딩은 가역적인 형식 지정이고, 검증은 암호화 유효성 검사에 정책 적용을 더한 것입니다.
실제 사고에서 일반적인 실패 패턴은 문자 그대로의 경쟁이 아닌 '선사용 후검증'입니다. 애플리케이션이 토큰을 디코딩하여 다음과 같이 읽습니다. 역할, user_id, 테넌트또는 범위서명 확인 및 클레임 유효성 검사가 최종적으로 시행되기 전에 권한 부여 결정을 내립니다. JWT 테스트에 대한 OWASP 지침은 알고리즘 기대치와 검증 로직을 잘못 처리하면 비대칭 검증 흐름과 대칭 검증 흐름 간의 혼동을 포함하여 영향력이 큰 공격이 어떻게 발생하는지 강조합니다. (OWASP 재단)
서명이 유효하더라도 토큰은 여전히 클레임 유효성 검사가 필요합니다. RFC 7519는 다음을 정의합니다. aud 토큰을 처리하는 주체가 자신을 의도된 대상자로 식별하지 않는 경우 다음과 같은 경우 토큰을 거부해야 한다고 명시하고 있습니다. aud 가 존재합니다. (IETF 데이터트래커) 이는 암호화 유효성이 컨텍스트 유효성과 동일하지 않다는 점을 상기시켜 줍니다.
고급 익스플로잇: "alg: none" 그 이상
"alg: none" 내러티브는 역사적으로 중요하지만, 대부분의 최신 라이브러리는 기본적으로 이를 차단합니다. 오늘날 더 흥미로운 실패는 암호화 공급자의 구현 결함과 유연한 검증 파이프라인으로 인한 복잡한 로직 우회라는 두 가지 버킷에 속하는 경향이 있습니다.
심령 서명(CVE-2022-21449): ECDSA 검증이 거짓인 경우
"심령 서명"으로 널리 알려진 CVE-2022-21449는 영향을 받는 Java 버전(특히 Java 15에 도입되어 2022년 4월 CPU에서 수정됨)의 특정 조건에서 ECDSA 서명 확인을 우회할 수 있는 심각한 Java 암호화 취약점입니다. 분석 결과, 이 취약점은 ECDSA 서명에 의존하는 시스템을 얼마나 크게 약화시키는지 강조하며, 여기에는 ECDSA 서명 JWT 또는 WebAuthn 메커니즘과 관련된 시나리오가 포함됩니다. (닐 매든)
토큰 보안에서 가장 중요한 교훈은 우회 방법의 영리함이 아니라 "ES256을 선택했다"는 것이 보장이 되지 않는다는 운영상의 현실입니다. 런타임 버전, 보안 공급자 구현, 패치 상태는 위협 모델의 일부입니다. 보안팀은 '암호화 공급자 회귀'를 1급 위험으로 취급하고, 명시적인 패치 SLA와 회귀 테스트를 통해 잘못된 서명에 대한 검증을 수행해야 합니다.
알고리즘 혼동/키 혼동: 서버가 토큰이 규칙을 선택하도록 허용하는 경우
알고리즘 혼동 공격은 검증자가 토큰이 검증에 사용되는 알고리즘에 영향을 미치도록 허용하고 키 처리가 비대칭 모드와 대칭 모드를 명확하게 분리하지 않을 때 발생합니다. 포트스위거는 이 경우를 제대로 처리하지 않으면 공격자가 서버의 비밀 서명 키를 알지 못한 채 임의의 값을 포함하는 유효한 JWT를 위조할 수 있다고 지적합니다. (포트스위거)
인증 결정이 내려지는 경계에서 '알고리즘 민첩성'을 허용하지 않는 것은 개념적으로는 간단하지만 실제로는 자주 놓치는 방어책입니다. 서비스에서 RS256을 기대하는 경우 RS256을 적용합니다. "헤더에 있는 알고리즘이 무엇이든 수락하고 유효성을 검사"하지 않습니다.

헤더 기반 키 검색: kid/jku 검증 공급망으로
헤더가 공격자 입력이라는 것을 수락하면 키 선택이 공격 표면의 일부라는 것도 수락하는 것입니다. A kid 는 런타임에 가져오고 로드하거나 구성하는 임의의 키 자료가 아니라 미리 정의된 키 허용 목록에 매핑되어야 합니다. A jku 토큰이 신뢰 앵커의 출처를 재정의하는 것을 허용해서는 안 됩니다. OIDC에 대한 JWKS 가져오기를 지원하는 경우 신뢰할 수 있는 발급자 구성에 바인딩하고 허용 목록, 캐싱 및 모니터링으로 강화해야 합니다.
생산량 변동에도 살아남는 강화 전략
JWS 유효성 검사에 대한 심층 방어 접근 방식은 서류상으로는 지루해 보이지만, 대부분의 토큰 오류를 방지하는 방법입니다.
허용된 알고리즘을 명시적으로 고정하고 검증자가 예상되는 알고리즘이 사용되었는지 확인하도록 요구합니다. OWASP의 Java용 JWT 지침은 예방 조치로 이 점을 직접 언급하고 있습니다. (OWASP 치트 시트 시리즈)
발급자와 수신자를 일관되게 검증합니다. RFC 7519의 등록 클레임에 대한 정의는 학문적인 것이 아니라 "유효한 서명, 잘못된 컨텍스트"가 일반적인 실패 유형이기 때문에 존재합니다. 특히 대상 불일치는 다른 서비스용으로 발행된 토큰을 실수로 수락하는 가장 쉬운 방법 중 하나입니다. (IETF 데이터트래커)
키 ID를 조회 쿼리가 아닌 데이터로 취급합니다. A kid 는 파일 시스템 경로나 신뢰할 수 없는 입력으로 구축된 데이터베이스 쿼리가 아닌 경계 매핑(KMS 별칭, 정적 키 레지스트리 또는 엄격하게 제어되는 키 저장소)을 통해 해결해야 합니다.
암호화 런타임을 적극적으로 패치하고 알려진 치명적인 클래스에 대해 테스트합니다. CVE-2022-21449는 "올바른 알고리즘 선택"이 잘못된 검증 구현을 보완할 수 없음을 상기시키기 위해 존재합니다. (닐 매든)
활성 프로빙을 암시하는 이상 징후를 모니터링합니다. 대량의 base64url 패딩 오류, 반복되는 유효하지 않은 토큰 또는 높은 이탈률의 경우 kid 값은 지속적인 퍼징 또는 혼동 시도를 나타낼 수 있습니다. 모니터링을 한다고 해서 버그가 해결되는 것은 아니지만 탐지 시간을 단축하고 의심스러운 활동을 특정 엔드포인트 및 인증자와 연관시키는 데 도움이 될 수 있습니다.
검증 로직 감사 자동화: 펜리전트가 적합한 분야
실제 환경에서 어려운 부분은 토큰을 한 번 디코딩하는 것이 아닙니다. 토큰이 허용되는 위치를 발견하고, 어떤 서비스가 어떤 검증 규칙을 적용하는지 파악하고, 다운스트림 인증이 디코딩된 클레임에 의존하는지 조기에 증명하는 것입니다. 이러한 '디코딩 → 해석 → 변경 → 영향 검증' 루프는 반복적인 작업으로, 최신 AI 지원 보안 플랫폼이 산업화에 도움을 줄 수 있는 작업입니다.
오프라인 디코딩을 수행하여 토큰을 분류하고 고신호 필드(발급자, 대상, 범위, 역할)를 추출하고, 응답 행동과 서비스 지문에서 검증 스택을 추론한 다음, 정책 드리프트 알고리즘 허용 목록 오류, 마이크로서비스 전반에서 일관되지 않은 발급자/대상 적용, 안전하지 않은 키 선택 패턴을 체계적으로 테스트할 수 있는 Penligent 같은 플랫폼은 토큰 중심 테스트를 위한 자동화 계층으로 신뢰성 있는 위치를 차지할 수 있습니다. "마술처럼 토큰을 깨는 토큰"이 아니라 반복 가능한 증거 생성 및 지속적인 회귀 검사를 통해 미묘한 검증 회귀를 사전에 포착하는 것이 중요합니다.
JWS 검증을 보안에 중요한 API 경계로 취급하는 경우 AI 기반 워크플로는 릴리스, 환경 및 서비스 소유자 간에 경계를 일관성 있게 유지하는 데 도움이 될 때 가장 강력합니다.

결론
다음 명령은 JSON 웹 서명 디코딩 는 결승선이 아니라 출발선입니다. 디코딩은 통합 가시성을 제공하지만 보안은 엄격한 검증 및 클레임 유효성 검사, 체계적인 키 관리, 탄력적인 런타임 위생에서 비롯됩니다.
"심령 서명"(CVE-2022-21449)과 같은 치명적인 암호화 공급자 장애부터 알고리즘 혼란 및 헤더 기반 키 공급망 위험에 이르기까지 JWS 보안은 시스템 속성입니다. 신중한 수동 분석과 인증 드리프트를 감지하는 자동화를 결합하는 팀은 생태계가 진화하고 새로운 실패 모드가 등장하더라도 인증 계층을 견고하게 유지할 수 있습니다.
신뢰할 수 있는 리소스 및 추가 자료
RFC 7515: JSON 웹 서명(JWS). (RFC 편집기)
RFC 7519: JSON 웹 토큰(JWT). (IETF 데이터트래커)
포트스위거: 알고리즘 혼동 공격. (포트스위거)
OWASP WSTG: JSON 웹 토큰 테스트. (OWASP 재단)
OWASP 치트 시트: 자바용 JSON 웹 토큰. (OWASP 치트 시트 시리즈)
닐 매든: 자바의 심령 서명. (닐 매든)
CVE-2022-21449에 대한 JFrog 분석. (JFrog)
CVE-2022-21449에 대한 암호학적 설명. (암호 수학)

