
JWT는 JSON Web Token의 약자로 Web 환경에서의 안전한 정보 전송을 위한 개방형 표준으로 JSON 객체 형태로 정의된다.
개발자가 정의한 JWT가 사용되는 경우는 다음과 같다.
JWT 는 특히 사용자 인증 기능에 가장 많이 사용된다. 그래서 가장 많이 사용되는 인증 방식들을 한번 비교 해보았다.
Cookie는 유의미한 값을 갖지 않음Cookie는 Session Storage에서 가져온 Session ID를 담는 역할Cookie를 탈취함Cookie를 이용해 서버에 HTTP 요청을 보냄| Cookie | Session/Cookie | JWT | |
|---|---|---|---|
| 보안 | 취약 | 강함 | 강함 |
| 서버 부하 | 낮음 | 높음 | 낮음 |
| 네트워크 부하 | 높음 | 낮음 | 높음 |
JWT는 JSON 정보가 Base64Url로 인코딩된 형태가 점(.)으로 구분된 세 부분으로 구성되며, 각 부분은 다음과 같다.
세 부분이 각각 인코딩되어서 다음과 같은 형태를 띤다.
xxxxx.yyyyy.zzzzz
Header는 일반적으로 해시 알고리즘(HMAC SHA256 또는 RSA 등)과 토큰 유형을 정의한다.
{
"alg": "HS256",
"typ": "JWT"
}
Payload는 전달하고자 하는 데이터에 관한 내용이 포함된다.
데이터의 Key를 claim이라고 하며 세 가지 종류로 나뉜다.
claim으로 압축을 위해 세 글자로만 구성된다.iss (issuer), exp (expiration time), sub (subject), aud (audience) 등claim이다.claim{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
Signature는 인코딩된 Header, Payload와 Secret Key가 합쳐진 형태로 생성된다.
예를 들어, HMAC SHA256 알고리즘을 사용하는 경우 다음과 같은 방식으로 생성된다.
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
세 부분이 각각 Base64Url로 인코딩되어 다음과 같은 형태로 보여진다.

jwt.io debugger를 사용해서 실제로 생성해볼 수 있다.
위에서 언급하였듯이 JWT는 만능이 아니다. 다른 인증 방식에 비하여 서버 부하가 적고, 보안에 강하기 때문에 많이 사용되고, 권장하는 방식이지만 무턱대고 사용하는 경우 큰 보안 이슈가 발생한다.
{
"alg": "none",
"typ": "JWT"
}
위와 같이 Header의 해시 알고리즘 부분을 “none”으로 해두는 경우이다.
어떤 멍청한 개발자가 이렇게 하냐 싶겠지만 생각보다 많은 사람들이 실수하는 부분이기 때문에 한번 더 확인하는 것을 추천드린다.
실제로 “none”을 집어넣고 해킹을 시도하는 경우도 존재하기 때문에 이를 방지하기 위해서 “none”인 경우를 걸러주는 로직도 구현할 필요가 있어보인다.
JWT는 인코딩된 문자열이기는 하지만 디코딩이 매우 쉽다.
그렇기 때문에 JWT에는 절대로 민감한 유저 정보를 넣어서는 안되고, 해당 유저임을 확인할 수 있는 최소한의 정보만 넣어야 한다.
Secret Key는 당연하게도 어렵게 만들어야 한다.
로그인에 필요한 비밀번호와 같은 것이기 때문에 유추가 쉬운 문자는 당연히 배제해야 하고, 최대한 길게 생성하고, 생성된 키는 철저하게 관리해야 한다.
이게 조금 불안하다 싶으면 많은 사람들이 사용하는 방법이 존재한다.
생성과 검증에 필요한 공개 키와 비밀 키를 생성하는 것이다. 좀 더 쉽게 설명하자면 매우 풀기 어려운 수학적 문제(공개 키)와 출제자만 알고 있는 특정한 종류의 정보(비밀 키)를 생성한다. 문제(공개 키)를 풀면 원래의 메시지(비밀 키)를 해독할 수 있다.
이 방법을 사용하면 하나의 키를 사용할 때보다 키의 유출 위험이 줄어들고, 통신의 비밀성을 보장할 수 있다. 또한, 네트워크 규모가 커질수록 관리가 훨씬 용이해진다.
Session/Cookie 방식과 비슷하게 토큰을 탈취하는 경우가 발생할 수 있다. JWT는 클라이언트에서 관리되기 때문에 서버에서 이 문제를 해결할 수 있는 방법은 없다. 이를 방지하기 위해 유효기간이 존재하지만, 만료될 때까지 기다려야 한다는 문제가 있다. 그래서 이를 해결하기 위한 좋은 방법이 존재한다.
기본적인 개념은 다음과 같다

해당 방법을 사용한 통신 과정은 다음과 같다.
Refresh Token과 Access Token 두 개를 서버로부터 받는다.Refresh Token과 Access Token을 로컬에 저장해놓는다.Access Token의 유효기간이 만료되었다.401를 통해 클라이언트는 invalid_token (유효기간이 만료되었음)을 알 수 있다.Refresh Token을 넣어 API를 재요청한다.Refresh Token도 만료되었다면 서버는 동일하게 401 error code를 보내고, 클라이언트는 재로그인해야한다.해당 방법은 Access Token 탈취 문제를 해결하고자 만든 것이기 때문에 Refresh Token에 탈취 문제를 해결한 것은 아니다. OAuth는 이를 해결하기 위해 Refresh Token Rotation을 제시한다.
클라이언트가 Access Token을 재요청할 때 Refresh Token도 새로 발급받는 것이다. 이렇게 하면 탈취된 Refresh Token은 더 이상 해당 사용자의 Token이 아니게 되는 것이기 때문에 탈취 위험에서 벗어날 수 있게 된다.
JWT, Introduction, https://jwt.io/introduction
코딩애플, JWT 대충 쓰면 님들 인생 끝남, https://www.youtube.com/watch?v=XXseiON9CV0
Wikipedia, 공개 키 암호 방식, https://ko.wikipedia.org/wiki/공개키암호_방식
OAuth, What Are Refresh Tokens and How to Use Them Securely, https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/