"로그인 후 서버가 건네주는 서명된 출입증" — 그것이 JWT입니다. 작동 원리와 안전하게 쓰기 위한 핵심을 살펴봅니다(공격 절차는 다루지 않습니다).
작동 원리(세 부분)
JWT는 header.payload.signature를 .로 이은 것입니다. header와 payload는 그저 base64url로 인코딩된 것이고(암호화 아님), 서명만 비밀(또는 공유) 키로 만들어집니다.
1. header
서명 알고리즘 같은 메타. 누구나 읽을 수 있음
2. payload
사용자 id, 만료 같은 클레임. 누구나 읽음 = 시크릿 금지
3. signature
키로 만들어짐. 변조 없음을 보장
핵심은 내용이 "그냥 읽힌다"는 점입니다. 그래서 payload는 비밀번호나 개인 데이터를 둘 곳이 아닙니다. JWT의 가치는, 서명을 검증함으로써 서버가 "내가 발급한 토큰이고 변조되지 않았다"를 확인할 수 있다는 데 있습니다.
안전하게 쓰기 위한 방어
항상 서명을 검증하고 alg를 고정(가장 중요)
매번 서버 측에서 서명을 검증하세요. 받아들이는 서명 알고리즘(alg)을 기대하는 값으로 고정하고, alg:none(서명 없음)을 거부하세요. 토큰 header에 적힌 alg를 무분별하게 신뢰하지 마세요.
payload에 시크릿을 넣지 않기
누구나 내용을 디코딩할 수 있다고 가정하세요. 시크릿 — 비밀번호, 개인 데이터, API 키 — 는 거기에 두지 마세요. 식별자나 권한처럼 보여도 치명적이지 않은 클레임만 포함하세요.
만료를 짧게 두고 폐기 방법을 마련
액세스 토큰을 짧은 수명으로 만드세요. 만료가 지나치게 길면 하나가 탈취됐을 때 피해 기간이 늘어납니다. 폐기가 필요하면, 짧은 수명의 액세스 토큰을 서버가 관리하는 리프레시/세션과 짝지으세요.
강한 키를 쓰고 탈취당하지 않는 곳에 보관
서명 키를 강하게 만들고, 재사용하지 말며, 서버 측 안전한 곳에 보관하세요. 토큰 자체도 보호하세요 — XSS로 탈취되지 않게 하고, 안전한 속성을 가진 쿠키에 담으세요(→ XSS를 통한 탈취에 주의).
본 사이트의 견해: JWT는 만능 세션이 아니다
JWT는 흔히 "로그인 상태를 유지"하는 데 쓰이지만, 약점이 있습니다: 폐기가 어렵습니다. 서명이 유효하고 만료되지 않았다면 서버는 기본적으로 그것을 신뢰하므로, 로그아웃이나 무효화를 즉시 반영하기가 까다롭습니다. 본 사이트의 입장은: JWT를 장수하는 만능 세션으로 만들지 말 것. 짧은 수명의 액세스 토큰을 서버 측의, 폐기 가능한 리프레시/세션과 결합하면 JWT의 장점(저렴한 검증)과 손쉬운 폐기를 모두 얻습니다. 용도에 맞게 골라 쓰세요.
맹점: "디코딩됐다"는 "유효하다"가 아니다
내용이 읽히기 때문에, 누구나 JWT를 JWT 디코더에 붙여 넣어 header와 payload를 들여다볼 수 있습니다. 하지만 디코딩(내용 읽기)과 검증(진위 확인)은 전혀 다릅니다. 디코딩할 수 있다는 것은 유효성에 대해 아무것도 보장하지 않습니다. 토큰이 진짜인지를 결정하는 것은 언제나 서버 측 서명 검증입니다. 디버깅을 위해 내용을 읽는 것은 편리하지만 — "읽을 수 있었다"를 "유효하다"로 착각하지 마세요.
다음으로 읽기
- 도구: JWT 디코더 / 인스펙터(내용 확인, alg:none이나 만료 점검)
- 용어집: XSS란(고전적 토큰 탈취 경로) / CSRF란
- 학습: MFA를 제대로 고르는 법
FAQ
QJWT의 내용은 암호화되어 있나요?
아니요. 표준 JWT의 header와 payload는 암호화가 아니라 base64url로 '인코딩'된 것이라, 토큰을 가진 누구나 내용을 읽을 수 있습니다. 그러니 비밀번호나 개인 데이터 같은 시크릿을 payload에 절대 넣지 마세요. JWT가 보호하는 것은 '내용의 기밀성'이 아니라 '변조되지 않았다는 것'(서명을 통한 무결성)입니다.
Q가장 위험한 JWT 설정은 무엇인가요?
서명을 검증하지 않거나, 'alg:none'(서명 없음)을 받아들이는 것입니다. 그것을 허용하면 공격자가 내용을 마음대로 고쳐 쓴 위조 토큰을 통과시킬 수 있습니다. 방어는 항상 서버 측에서 서명을 검증하고, 받아들이는 서명 알고리즘(alg)을 기대하는 값으로 고정하는 것입니다.
Q디코딩과 검증은 같은 것인가요?
아니요. 디코딩은 그저 내용을 읽는 것이고 누구나 할 수 있습니다(JWT 디코더로 확인 가능). 검증은 서버가 비밀/공개 키로 서명이 유효한지, 토큰이 만료되지 않았는지, 발급자/대상이 올바른지를 확인하는 것입니다. '디코딩됐다'가 '유효한 토큰이다'를 뜻하지 않으며 — 진위는 서버 측 검증이 결정합니다.