JWT 생성기
비밀 키는 이 페이지에서만 사용되며 전송, 저장되지 않고 URL에도 포함되지 않으므로 링크를 공유해도 유출되지 않습니다. 다만 운영 환경의 서명 키는 이 사이트를 포함한 어떤 웹사이트에도 붙여넣지 마세요. 테스트에는 임시 키를 사용하세요.
HS256, HS384, HS512만 지원합니다. RS, ES, PS 서명에는 개인 키가 필요하며 이 도구는 의도적으로 요구하지 않습니다.
입력한 내용은 저장, 기록, 공유되지 않습니다.
JWT 페이로드를 작성하고 알고리즘을 선택해 비밀 키로 서명합니다. 서명은 브라우저에서만 이루어지므로 키가 기기를 벗어나지 않습니다.
사용 방법
- 페이로드를 JSON으로 편집합니다.
- 필요하면 만료 기간을 선택하세요. iat와 exp가 자동으로 추가됩니다.
- 비밀 키를 입력하면 서명된 토큰이 아래에 표시됩니다.
서명이 만들어 내는 것
JWT는 base64url 세그먼트 세 개를 점으로 이은 것입니다. 첫 번째는 어떤 알고리즘을 썼는지 알려 주는 헤더, 두 번째는 클레임을 담은 페이로드, 세 번째는 앞의 두 세그먼트를 점으로 이은 것에 대해 계산한 서명입니다. 서명되는 것은 그 세그먼트들의 정확한 바이트이지 디코딩한 JSON이 아니며, 그래서 토큰을 다시 직렬화하면 무효가 됩니다.
HS256에서 서명은 그 서명 입력에 대해 비밀 키로 계산한 HMAC입니다. 같은 비밀 키를 가진 사람은 누구나 다시 계산해 일치하는지 볼 수 있으며, 이로써 두 가지가 증명됩니다. 토큰이 비밀 키를 아는 사람에 의해 발급되었다는 것, 그리고 그 이후 내용이 전혀 바뀌지 않았다는 것입니다. 페이로드의 한 글자만 바꿔도 다시 계산한 서명은 일치하지 않습니다.
하지만 서명은 아무것도 숨기지 않습니다. 페이로드는 인코딩되었을 뿐 암호화되지 않았으므로, 토큰을 가진 사람은 비밀 키 없이도 모든 클레임을 읽을 수 있습니다. 비밀번호, 카드 번호, 로그에 남기고 싶지 않은 그 밖의 어떤 정보도 JWT 페이로드에 넣지 마세요.
비밀 키 고르기
RFC 7518은 HMAC 키가 최소한 해시 출력 크기 이상이어야 한다고 요구합니다. HS256은 256비트, HS384는 384비트, HS512는 512비트입니다. 즉 외우기 쉬운 문구가 아니라 최소 32바이트의 무작위 값이어야 합니다.
이유는 JWT가 그 자체로 오프라인 공격 재료이기 때문입니다. 서명 입력과 서명이 함께 들어 있으므로, 토큰 하나를 손에 넣은 사람은 네트워크 왕복도 속도 제한도 없이 하드웨어가 허락하는 한 빠르게 후보 키를 대입해 볼 수 있습니다. 바로 그 일을 하는 도구가 공개되어 있으며, 사전 단어나 짧은 문자열은 몇 초 만에 뚫립니다. 약한 비밀 키를 쓴 토큰이 유출되면 공격자는 원하는 클레임을 넣은 토큰을 직접 발급할 수 있습니다.
비밀번호 관리자나 키 생성기로 만든 무작위 값을 쓰고, 스테이징 토큰이 운영 환경에서 재사용되지 않도록 환경마다 다른 비밀 키를 두며, 다른 자격 증명처럼 주기적으로 교체하세요.
설정해 둘 만한 클레임
애플리케이션에 필요한 것 외에도 RFC 7519는 소수의 표준 클레임을 등록합니다. iss는 발급자, sub는 주체, aud는 대상을 나타내고, jti는 토큰에 고유 ID를 부여해 개별적으로 폐기하거나 재사용 여부를 확인할 수 있게 합니다. 세 시간 클레임, 즉 만료를 뜻하는 exp, 가장 이른 유효 시점인 nbf, 발급 시각인 iat는 NumericDate 값으로, 유닉스 에포크 이후의 초를 뜻합니다.
밀리초가 아니라 초입니다. JavaScript 타임스탬프를 그대로 넣는 것이 직접 작성한 토큰이 거부되는 가장 흔한 단일 원인입니다. 13자리 exp는 먼 미래의 날짜로 해석되어, 어떤 라이브러리는 바로 거부하고 어떤 라이브러리는 영원히 만료되지 않는 토큰으로 받아들입니다.
exp를 설정한다고 토큰이 저절로 만료되지는 않습니다. 검증하는 쪽이 클레임을 확인하기 때문에 만료되는 것이며, 서명만 확인하는 검증기는 작년 토큰도 기꺼이 받아들입니다. 발급 머신과 검증 머신의 시계가 정확히 일치하는 경우는 드물기 때문에, 대부분의 라이브러리는 보통 1~2분 정도의 시계 오차도 허용합니다.
HS 알고리즘만 지원하는 이유와 alg 함정
HS 계열은 대칭 방식입니다. 공유된 비밀 키 하나로 서명과 검증을 모두 하므로, 입력한 내용만으로 여기서 서명할 수 있습니다. RS, ES, PS는 비대칭 방식이라 개인 키를 웹 페이지에 붙여 넣어야 하는데, 이 도구는 그런 요청을 할 생각이 없습니다.
알고리즘을 나타내는 헤더 필드는 잘 알려진 두 공격이 자리 잡은 곳이기도 합니다. 첫째는 토큰이 서명되지 않았다고 선언하는 alg: none입니다. 이를 받아들인 라이브러리에는 서명이 비어 있는 위조 토큰을 넘길 수 있었습니다. 둘째는 알고리즘 바꿔치기입니다. RS256으로 서명된 토큰의 헤더를 HS256으로 바꾸고, 발급자의 공개 키를 HMAC 비밀 키로 삼아 서명합니다. 알고리즘을 미리 정하지 않고 토큰에서 읽어 오는 검증기는 공개 키가 비밀이 아니므로 이를 문제없이 통과시킵니다.
두 경우의 방어책은 같으며, 발급 쪽이 아니라 검증 쪽에 있어야 합니다. 검증 호출에서 기대하는 알고리즘을 고정하고, 헤더가 그와 다른 토큰은 모두 거부하세요.
토큰의 수명 설계하기
토큰 시스템에서 가장 큰 영향을 미치는 선택은 토큰이 얼마나 오래 살아 있느냐입니다. JWT는 만료되기 전에는 회수할 수 없기 때문입니다. 액세스 토큰을 5분에서 15분처럼 짧게 두면 토큰이 유출됐을 때의 위험 구간이 작게 유지되며, 이것이 표준적인 답입니다. 여기에 서버가 저장하고 있어서 폐기할 수 있는, 수명이 더 긴 리프레시 토큰을 짝지어 씁니다.
리프레시 토큰 회전을 더하면 이 조합은 실질적으로 훨씬 안전해집니다. 갱신할 때마다 새 리프레시 토큰을 발급하고 이전 것을 무효화하므로, 탈취된 토큰은 한 번만 쓸 수 있습니다. 이전 토큰이 다시 제시되면 서버는 두 주체가 같은 토큰을 들고 있다는 사실을 알게 되고, 그 계열 전체를 폐기할 수 있습니다.
정말로 수명이 긴 자격 증명이 필요한 경우, 예를 들어 머신 간 연동이나 CLI 같은 경우에 JWT는 대개 맞지 않는 형태입니다. 데이터베이스에 대조해 확인하는 API 키라면 즉시 폐기할 수 있고, 권한 범위를 정밀하게 좁힐 수 있으며, 소유자가 마지막 사용 시각을 볼 수 있는 화면에 표시할 수도 있습니다.
키, 회전, 그리고 kid 헤더
서명 키는 서비스 중단 없이 교체할 수 있어야 하고, 그 장치가 바로 kid 헤더입니다. 발급자가 어떤 키로 서명했는지 밝히면 검증자는 짐작하지 않고 그 키를 찾아봅니다. HS256이라면 키 식별자와 비밀 값을 잇는 작은 맵이 되고, RS256이나 ES256이라면 검증자가 가져와 캐시하는 JWKS 문서가 됩니다.
회전은 세 단계로 이뤄집니다. 검증자가 알 수 있도록 새 키를 게시하고, 옛 키도 계속 받아들이면서 서명만 새 키로 바꾸고, 옛 키로 서명한 토큰이 모두 만료된 뒤에 그 키를 폐기합니다. 이 겹치는 기간을 건너뛰는 것이 평범한 회전을 장애로 바꿔 놓습니다.
토큰을 검증하는 서비스가 둘 이상이 되는 순간부터 비대칭 알고리즘은 추가 준비 비용을 치를 가치가 있습니다. HS256에서는 모든 검증자가 서명도 할 수 있는 비밀 값을 들고 있어서, 어느 한 서비스만 침해돼도 토큰을 찍어낼 수 있습니다. RS256에서는 검증자가 공개 키만 갖고 있으므로 아무것도 위조하지 못합니다.
페이로드에 넣을 것과 넣지 말아야 할 것
토큰은 모든 요청의 헤더에 실려 다니는 bearer 자격 증명이므로 크기에는 실제 비용이 따릅니다. 프록시는 헤더를 흔히 8 KB로 제한하고, 비대한 토큰의 대가는 호출할 때마다 치르게 됩니다. 신원과 검증자가 필요로 하는 몇 가지 사실, 즉 subject, issuer, audience, 만료 시각, 그리고 소수의 스코프나 역할 정도로 유지하십시오.
자주 바뀌는 값은 잘 맞지 않습니다. 토큰에 구워 넣은 권한은 발급된 순간부터 낡기 시작하므로, 10시에 회수한 역할도 토큰이 만료될 때까지는 계속 주장됩니다. 그것이 중요한 상황이라면 토큰은 신원만 담고 인가는 서비스가 조회하도록 하거나, 낡음이 용인될 만큼 수명을 짧게 잡아야 합니다.
비밀은 애초에 여기에 들어갈 자리가 없습니다. 페이로드는 인코딩된 것일 뿐 암호화된 것이 아니어서, 토큰을 가진 사람은 누구나 모든 클레임을 읽을 수 있습니다. 내부 메모도, 수신자에게 필요한 범위를 넘는 개인정보도, 키도 넣지 마십시오. 페이로드가 정말로 비밀이어야 한다면 그것은 JWE이며, 도구도 다른 별개의 명세입니다.
아무것도 흘리지 않고 토큰 시험하기
생성기가 가장 쓸모 있는 순간은 픽스처를 만들 때입니다. 만료된 토큰, 아직 유효하지 않은 토큰, audience가 틀린 토큰, 다른 키로 서명한 토큰이 그것입니다. 검증 루틴은 이 네 가지 모두에 대해 시험해야 하며, 이 영역의 버그는 대부분 그중 하나가 통과되는 경우입니다.
테스트용 비밀 값을 쓰고 운영용은 절대 쓰지 마십시오. 다른 자격 증명과 같은 논리지만 여기서는 더 날카롭습니다. 토큰과 그 서명만 있으면 비밀 값을 오프라인에서 공격하기에 충분하기 때문입니다. 환경마다 비밀 값을 분리해 두면 스테이징에서 찍어낸 토큰이 운영에서 받아들여지는, 더 미묘한 실패도 막을 수 있습니다.
이 페이지의 모든 동작은 브라우저 안에서 실행되므로 여기에 입력한 내용은 전송되거나 저장되지 않습니다. 사람들이 토큰 도구에 무엇을 붙여 넣는지를 생각하면 알아둘 가치가 있고, 비대칭 알고리즘이 없는 이유이기도 합니다. RS256으로 서명하려면 개인 키를 웹 페이지에 붙여 넣어야 하는데, 어떤 도구도 그것을 요구해서는 안 되기 때문입니다.
손으로 만든 토큰이 거부되는 흔한 이유
시간 클레임을 밀리초로 넣는 것이 가장 흔합니다. NumericDate는 에포크 이후의 초 단위이므로, 13자리 exp는 수만 년 뒤의 날짜로 읽힙니다. 어떤 라이브러리는 곧바로 거부하고 어떤 라이브러리는 영원히 유효한 토큰으로 받아들이는데, 둘 다 원하던 결과가 아닙니다.
다음은 패딩입니다. JWT 세그먼트는 패딩 없는 base64url을 쓰므로, 세그먼트가 등호로 끝나거나 빼기와 밑줄 대신 더하기와 슬래시가 들어 있는 토큰은 잘못된 알파벳으로 인코딩된 것이며 서명을 확인하기도 전에 실패합니다.
나머지는 대개 불일치입니다. 검증자가 기대하는 audience가 끝의 슬래시 하나만큼 다르거나, issuer가 http로 적혀 있는데 검증자는 https를 쓰거나, 한쪽 장비의 시계가 몇 분 어긋나 있는데 허용 오차가 설정돼 있지 않은 식입니다. 토큰이 제대로 디코딩되는데도 실패한다면 서명을 의심하기 전에 클레임과 검증자의 설정을 필드 하나하나 대조해 보십시오.
자주 묻는 질문
- 서명 키를 입력해도 안전한가요?
- 키는 이 페이지의 JavaScript에서만 사용됩니다. 전송되거나 저장되지 않고 URL에도 기록되지 않으므로 링크를 공유해도 유출되지 않습니다. 그래도 운영 환경의 서명 키는 비밀번호처럼 다루고, 테스트에는 임시 값을 사용하세요.
- 왜 HS256, HS384, HS512만 지원하나요?
- 대칭 방식이라 하나의 공유 비밀 키로 서명과 검증을 모두 할 수 있습니다. RS, ES, PS는 비대칭 방식이라 개인 키를 웹 폼에 붙여넣어야 하는데, 이 도구는 그것을 요구하지 않습니다.
- iat와 exp에는 무엇이 들어가야 하나요?
- 둘 다 NumericDate로, 밀리초가 아니라 유닉스 에포크 기준 초 단위입니다. 여기서 만료 기간을 선택하면 올바른 형식으로 기록됩니다. 밀리초로 넣는 것이 직접 만든 토큰이 거부되는 가장 흔한 원인입니다.
- 헤더에 사용자 정의 필드를 추가할 수 있나요?
- 헤더는 선택한 알고리즘과 typ: JWT로 설정됩니다. kid 같은 항목은 특정 키 관리 방식에 종속되므로 실제 토큰을 발급하는 라이브러리에서 추가하는 편이 적절합니다.
- 여기서 만든 토큰을 운영 환경에 사용해도 되나요?
- 아닙니다. 이 도구는 테스트와 디버깅용입니다. 실제 토큰은 키 교체, 폐기, 클레임 검증을 관리하는 인증 서비스에서 발급해야 합니다.
마지막 업데이트:
진행 단계, 예상 결과, 실제 결과를 적어 주세요. 비밀 정보와 개인 정보는 삭제해 주세요.
