JWT 탭은 JSON Web Token을 위한 워크벤치입니다. 토큰을 디코드하고, claim을 편집해 재서명하며, 서버를 상대로 테스트할 고전적인 공격 페이로드를 생성합니다. 파트를 보여주기만 하는 Decoder의 읽기 전용 jwt-decode 컨버터보다 한 걸음 더 나아갑니다.
어디서든(예: History 상세 패널, Notes 등) 토큰을 선택하고 Space → Send to JWT를 누르면 그 토큰으로 새 워크벤치 서브탭을 채웁니다. 세션은 휘발성이라 디스크에는 아무것도 기록되지 않습니다. 이 탭은 기본적으로 바 밖에 있습니다. **0**을 누르고 "jwt"를 입력하거나, 커맨드 팔레트(Ctrl-P → Go to JWT)를 쓰거나, Preferences에서 슬롯을 내주세요.
두 개의 렌즈
하나의 세션, 두 개의 뷰이며 Ctrl-T로 전환합니다. 각 렌즈의 최상위 카드 테두리에 전환 칩이 있습니다(INPUT에는 ^T:→ENCODE, HEADER에는 ^T:→DECODE). 클릭해도 키와 똑같이 동작합니다:
- Decode: INPUT에 토큰을 붙여 넣으면 header, payload, signature가 실시간으로 디코드됩니다. 그 아래에는 생성된 공격 페이로드를 고를 수 있는 목록이 있습니다.
- Encode: HEADER와 PAYLOAD를 JSON으로 편집하고, 알고리즘을 선택하며(
Ctrl-A로 HMAC 계열 →RS/PS/ES의 256/384/512 →EdDSA→none순환), SECRET을 설정하면 재서명된 토큰이 OUTPUT에 실시간으로 나타납니다. 비대칭 알고리즘에서는 카드가 KEY로 바뀌며, 입력한 시크릿 대신 PEM 개인키 경로를 받습니다.
Space → Load decoded claims는 현재 Decode 쪽에서 디코드된 토큰을 Encode 편집기로 불러옵니다. 그래서 claim 하나를 손보고 두 동작만으로 재서명할 수 있습니다. 결과는 y로 복사하세요.
signature는 디코드되어 표시되지만 Decode 쪽에서는 검증되지 않습니다. 따라서 디코드는 토큰이 무엇을 주장하는지 알려줄 뿐, 신뢰할 수 있는지는 알려주지 않습니다. Encode는 지정한 키와 알고리즘으로 실제로 서명하고, 키를 가지고 있다면
gori run jwt --verify(또는 MCPjwt_verify)가 신뢰 여부에 답합니다.
알고리즘
HMAC(HS256 / HS384 / HS512)은 직접 입력한 시크릿으로 서명합니다. 비대칭 계열은 사용자가 제공한 PEM 키로 서명합니다 — gori는 키를 만들지 않으므로, RS256 토큰을 위조하려면 여전히 RS256 개인키가 필요합니다:
| 계열 | 알고리즘 | 키 |
|---|---|---|
| HMAC | HS256 HS384 HS512 |
공유 시크릿을 그대로 입력 |
| RSA PKCS#1 | RS256 RS384 RS512 |
서명은 RSA 개인키, 검증은 공개키 / 인증서 |
| RSA-PSS | PS256 PS384 PS512 |
같은 RSA 키. salt 길이는 다이제스트 길이(RFC 7518 §3.5) |
| ECDSA | ES256 ES384 ES512 |
P-256 / P-384 / P-521 키. 서명은 JOSE가 규정한 고정 폭 r‖s 형식이라 ES512는 128이 아니라 132바이트입니다 |
| EdDSA | EdDSA |
Ed25519 키 |
| 미서명 | none |
키 없음 — 의도적으로 제공하는 인증 우회 형태 |
키는 PEM 본문을 그대로 넣거나 .pem 파일 경로로 지정합니다. 검증에는 X.509 인증서(IdP가 x5c로 공개하는 것)나 개인키 자체도 받습니다.
암호화된 토큰(JWE)
header.encrypted_key.iv.ciphertext.tag 형태의 5-세그먼트 토큰은 JWE, 즉 암호화된 JWT입니다. gori는 JWS를 인식하는 모든 곳(JWT 탭, Decoder, 본문 pretty-printer, gori run jwt, MCP jwt_decode)에서 JWE도 인식하고 보호 헤더를 보여줍니다: 콘텐츠 암호화 키를 감싼 alg, 클레임을 암호화한 enc, 그리고 kid.
gori는 복호화하지 않습니다. 키가 없으면 클레임은 암호화됨으로 표시되고 그 자리에 아무것도 추측해 넣지 않으며, 클레임 위에서만 의미가 있는 투영은 토큰을 아예 거부합니다: --attacks는 아무것도 생성하지 않고(조작할 클레임 세그먼트도, 제거할 서명도 없습니다), --verify는 지원하지 않는 알고리즘이라고 말하는 대신 그 사실을 그대로 알려줍니다.
공격 페이로드
디코드된 토큰에서 gori는 흔한 JWT 검증 결함을 찔러보는, 바로 전송 가능한 변형을 생성합니다:
| 공격 | 무엇을 테스트하는가 |
|---|---|
| alg:none | signature를 제거하고 alg를 none으로 설정합니다(그리고 None / NONE / nOnE 대소문자 변형 포함). 서명 없는 토큰을 받아들이는 서버를 겨냥합니다. 원래 header를 유지하는 페이로드가 둘 더 있습니다: signature stripped(세 번째 세그먼트가 빈 토큰)와 no signature segment(세그먼트가 둘뿐인 토큰). |
| Weak secret | 흔한 약한 HMAC secret 목록으로 토큰을 재서명해, 추측 가능한 서명 키를 잡아냅니다. |
| Header injection | kid, jku, x5u, jwk header 파라미터를 조작합니다. 공격자가 제공한 키 자료를 신뢰하는 서버를 겨냥합니다. |
| Algorithm confusion | 서버의 공개키를 줬을 때만 생성됩니다(--attacks --key, MCP public_key). RS/PS/ES 토큰을 HS256으로 낮추고 공개키 바이트 자체를 HMAC 키로 써서 서명합니다. 토큰의 alg로 분기하면서 검증 키를 그대로 재사용하는 서버를 겨냥합니다. 서버가 가지고 있을 법한 표기마다 페이로드를 하나씩 만듭니다: OpenSSL이 쓰는 정규 SPKI PEM, 그리고 준 그대로의 바이트를 끝 개행 있는 형태와 없는 형태로. |
후보를 Repeater로 보내 대상을 상대로 시도하거나, 이미 편집 중인 요청에 곧바로 넣으세요.
헤드리스
gori run jwt eyJhbGci... # decode (default)
gori run jwt eyJhbGci... --encode --alg HS256 --secret s3cret
gori run jwt eyJhbGci... --encode --set role=admin --secret s3cret # claim 하나를 패치해 재서명
gori run jwt eyJhbGci... --encode --payload '{"sub":"1","admin":true}' --secret s3cret # claim 전체를 교체
gori run jwt eyJhbGci... --encode --alg ES256 --key ./private.pem # 비대칭 키로 서명
gori run jwt eyJhbGci... --verify --key ./public.pem # 이 토큰은 유효한가?
gori run jwt eyJhbGci... --attacks # print the attack payloads
gori run jwt eyJhbGci... --attacks --key ./public.pem # ...algorithm confusion까지
cat token.txt | gori run jwt --attacks # token from stdin
토큰은 인자나 stdin에서 옵니다. 프로젝트나 캡처는 관여하지 않습니다(순수한 로컬 연산입니다). --format은 text 또는 json입니다. --encode에서 --set KEY=VALUE는 claim 하나를 패치하고(반복 가능. 값이 JSON으로 파싱되면 그 타입을 유지하므로 admin=true는 불리언, role=admin은 문자열입니다), --payload JSON은 claim 객체 전체를 교체합니다. 둘은 상호 배타적입니다. --secret과 --key는 같은 자리를 채우므로 하나만 넘기세요. --verify는 verified: yes|no를 출력하고 어느 쪽이든 0으로 종료합니다 — "no"도 답이므로 스크립트는 종료 코드가 아니라 이 필드를 읽어야 합니다. CLI Reference를 참고하세요.
MCP에서는 jwt_decode / jwt_verify / jwt_encode / jwt_attacks가 --read-only에서도 사용할 수 있는 read 도구입니다. 네트워크를 건드리지 않고 아무것도 쓰지 않습니다. jwt_encode도 같은 set / payload claim 편집과 같은 key를 받고, jwt_attacks는 public_key를 받습니다. 다만 key / public_key는 경로도 받으므로 이 두 인자는 호출자가 지정한 파일을 읽습니다 — 클라이언트가 파일시스템에 닿지 않아야 한다면 PEM을 인라인으로 넘기세요.
다음 단계
- Decoder: 더 긴 변환 체인 안에서 JWT를 디코드합니다
- Sequencer: JWT가 아닌 토큰의 무작위성을 평가합니다
- Repeater & Fuzzer: 공격 페이로드를 대상에 발사합니다