민감한 JWT, 이제 안심하고 디버깅! 내 토큰이 서버 밖으로 나가지 않는 유일한 툴
2026. 7. 23.
민감한 JWT, 이제 안심하고 디버깅! 내 토큰이 서버 밖으로 나가지 않는 유일한 툴
온라인 JWT 디버거를 써본 사람이라면 누구나 한 번쯤은 페이지 한쪽에 쓰여 있는 경고 문구를 봤을 겁니다. "절대 운영 환경 토큰을 붙여넣지 마세요!" 하지만 솔직히, 누가 그 말을 듣던가요? 토큰이 문제를 일으켜서 이 툴을 열었다면, 그 토큰은 당연히 실제 운영 중인 토큰일 테니까요. 결국 우린 무심코 붙여넣고, 우리의 민감한 세션 토큰은 어딘가 낯선 서버의 로그에 고스란히 남게 됩니다. 이 문제 때문에 저 역시 꽤나 짜증이 나서 직접 대안을 만들었습니다. 바로 'TokenBench'입니다.
제가 스스로에게 내건 규칙
제가 TokenBench를 만들면서 세운 가장 중요한 원칙은 딱 하나였습니다. '당신이 입력하는 어떤 것도 페이지를 벗어나지 않는다.' "저장하지 않아요!"나 "30초 후에 삭제됩니다!" 같은 애매한 말은 하지 않습니다. 애초에 받을 서버가 없으니 아무것도 나갈 일이 없는 거죠. 모든 작업은 브라우저 내의 WebCrypto API를 통해 이루어집니다. 디코딩, 서명 검증, 키 파싱, 토큰 생성까지 — 이 모든 과정이 100% 클라이언트 사이드에서 처리됩니다. 개발자 도구를 열고 네트워크 탭을 켜둔 채 토큰을 붙여넣어 보세요. 아무런 네트워크 요청도 발생하지 않는 것을 직접 확인할 수 있습니다. 탭을 닫아도 마찬가지고요. 제가 실무에서 토큰 디버깅을 하다가 실수로 중요한 환경의 토큰을 외부 툴에 붙여넣을 뻔했던 아찔한 경험이 있거든요. 그래서 이런 '오프라인' 작업 환경의 중요성을 더욱 체감합니다. 이 마지막 부분이 생각보다 훨씬 중요합니다.
하나의 결과가 아닌, 두 가지 판정
사실 TokenBench를 만들게 된 결정적인 계기는 프라이버시 문제 이전에, 어느 날 오후 저를 괴롭혔던 끔찍한 경험 때문이었습니다. 토큰 하나가 계속 실패했는데, 제가 쓰던 디버거는 그저 빨간색 오류 배너만 보여줬습니다. 빨간색 배너는 보통 서명 문제 아닌가요? 그래서 저는 서명 키를 확인하고, 키를 교체하고, 배포 설정을 확인하고, 심지어 스테이징과 프로덕션 환경이 시크릿을 공유하고 있는지까지 샅샅이 뒤졌습니다. 그런데 서명은 전혀 문제가 없었습니다. 단지 토큰이 만료되었을 뿐이었죠. 서명 실패와 토큰 만료는 완전히 다른 유형의 문제이며, 해결책도 천지차이입니다. 그런데 이 두 가지를 단 하나의 빨간색 배너로 뭉뚱그려 보여주는 바람에 한 시간을 허비했습니다. 그래서 TokenBench는 이 두 가지 결과를 항상 명확하게 분리해서 보여줍니다:
- 서명(Signature): 제공된 키와 토큰의 서명이 일치하는가
- 시간(Time):
exp(만료) /nbf(사용 시작) 기간 내에 있는가
실제로 가장 흔하게 발생하는 상황은 '올바르게 서명되었지만 만료된 토큰'입니다. 이런 토큰이 위조된 것처럼 보이게 해서는 안 된다고 생각했습니다.
알고리즘 혼란, 그리고 도움 주기를 거부하다
TokenBench에 HS256 토큰과 공개 키를 넘겨주면, 검증을 시도하지 않고 왜 안 되는지 설명해 줍니다. RS256은 개인 키로 서명하고 공개 키로 검증합니다. 그런데 이 공개 키는 말 그대로 '공개'되어 있죠. 만약 서버가 토큰 헤더에서 알고리즘 정보를 읽어와서 그에 따라 검증 로직을 분기한다면, 공격자는 헤더를 HS256으로 바꿔버리고, 여러분의 공개 키 바이트를 HMAC 시크릿으로 사용하여 토큰에 서명할 수 있습니다. 그렇게 되면 여러분의 코드는 누구나 다운로드할 수 있는 정보로 위조된 토큰을 아무렇지도 않게 검증해 버리는 끔찍한 일이 벌어집니다.
// 이렇게 하지 마세요 (Don't)
const header = JSON.parse(base64url.decode(token.split('.')[0]));
jwt.verify(token, publicKey, { algorithms: [header.alg] });
// 이렇게 하세요 (Do)
jwt.verify(token, publicKey, { algorithms: ['RS256'] });
알고리즘 선택은 토큰의 헤더가 아니라 개발자인 여러분의 결정이어야 합니다. 혼란스러운 (그리고 위험한) 작업을 묵묵히 수행하는 툴은 버그를 조장하는 것이나 다름없다고 생각합니다. 그래서 TokenBench는 그런 동작을 허용하지 않습니다. 이 툴에는 위조된 서명이나 alg: none 토큰을 의도적으로 생성할 수 있는 제너레이터 기능도 포함되어 있습니다. 이를 통해 여러분의 서버가 이런 악성 토큰들을 실제로 잘 거부하는지 직접 테스트해 볼 수 있습니다.
제가 잘못 생각했던 부분
처음 TokenBench를 만들었을 때는 분석(analytics) 기능이 있었습니다. 서드파티가 아닌 제가 직접 만든 퍼스트파티 분석이었고, 매우 신중하게 설계했습니다. 단순히 카운터만 집계하고, 이벤트 이름도 허용 목록으로 하드코딩했으며, 토큰에서 파생된 어떤 정보도 페이로드에 물리적으로 포함될 수 없도록 했습니다. 모든 데이터는 메모리에 보관되다가 페이지를 닫을 때 작은 비콘 하나만 전송되도록 했죠. 토큰을 붙여넣는 행위 자체로는 아무것도 전송되지 않도록 말입니다. 저는 이 설계에 꽤 만족했습니다. 충분히 방어할 수 있는 수준이라고 생각했죠.
그러다 릴리즈 전 마지막 점검을 하면서 네트워크 탭을 열고 앉아 그 비콘이 전송되는 것을 지켜봤습니다. 페이지에는 **"아무것도 전송되지 않습니다"**라고 쓰여 있는데, 뭔가가 전송되고 있던 겁니다. 비록 해가 없는 데이터였지만, 그것은 중요하지 않았습니다. 이 툴의 핵심 주장은 "나를 믿지 말고 직접 확인하세요"인데, 실제로 확인해 본 첫 번째 사용자부터 각주가 필요한 주장임을 발견하게 될 테니까요. 저도 예전에 제 블로그에 간단한 방문자 수 카운팅을 넣으려다 개인정보 처리 방침 문구 하나 때문에 며칠 밤낮을 고민했던 기억이 납니다. 결국 '간단한 카운팅'도 결국은 개인정보와 엮일 수 있다는 걸 깨닫고 그 복잡한 과정을 포기했죠.
그래서 결국 분석 기능은 완전히 사라졌습니다. 애널리틱스도, 비콘도, 쿠키도 없습니다. 2004년처럼 호스트 서버 로그에서 페이지뷰를 집계하고 있으며, 이제 "아무것도 전송되지 않는다"는 주장은 어떠한 각주도 없이 진실이 되었습니다. 이상하게 들릴지 모르지만, 저는 이 부분이 가장 자랑스럽습니다. 설명이 필요한 프라이버시 주장은 진정한 프라이버시 주장이 아니라 마케팅 문구에 불과하니까요.
TokenBench에는 무엇이 들어있나요?
TokenBench에는 네 가지 핵심 도구가 포함되어 있습니다: 디코더, 유효성 검사기, 생성기, 그리고 시크릿 키 생성기입니다. HS/RS/PS/ES 256/384/512 및 EdDSA를 포함한 모든 JOSE 알고리즘을 지원하죠. 키는 일반 시크릿, PEM (SPKI 및 PKCS#1), X.509 인증서, JWK 또는 JWKS 형식으로 입력할 수 있습니다. 토큰은 실제 환경에서 깔끔하게 넘어오는 경우가 드물다는 점을 감안해, 'Bearer' 접두사, 줄 바꿈, URL 인코딩 등은 자동으로 제거해 줍니다.
또한, 취약한 시크릿, alg: none, exp 누락, 튜토리얼에서 흔히 쓰이는 약한 시크릿 등 보안 관련 '린팅' 기능도 곳곳에 녹아 있습니다. 한 번 로드하면 오프라인에서도 완벽하게 작동하고, 무료이며, 별도의 가입 절차도 없습니다.
지금 바로 tokenbench.dev에서 경험해 보세요. 혹시 위 내용들이 의심스럽다면, 직접 제 말을 검증해 볼 수 있도록 GitHub에 소스 코드를 공개해 두었습니다. 혹시라도 부족한 점이 있다면 언제든 의견을 들려주세요.
원문: https://dev.to/mike_0f35516d33902a0d2546/every-jwt-debugger-tells-you-not-to-paste-real-tokens-so-i-built-one-where-it-doesnt-matter-3cf3 수집일: 2026-07-23 01:26:21