JWT 토큰 인증 방식 살펴보기
JWT Token 이란 무엇인가?
Facebook, Github, Google 등의 외국계 회사들의 Open API 를 통한 회원인증을 구현해봤다던가 혹은 카카오 API 등을 이용해본적이 있다면 한번쯤은 JWT Token 에 대해서 들어봤을 것이다. JWT Token 은 JSON Web Token 의 약자로서, 자체의 토큰은 Base64로 인코딩한 평범한 String 으로 이루어져 있다. 이 JWT Token 은 access token 을 만들기 위해 사용된다. 물론 여기에서 이야기 하는 access token 이란 단순하게 우리가 분류하는 자원에 접근하는 access token 만을 이야기 하는 것이 아니라, 권한/인증에 대한 token 을 말한다. JWT Token 은 그 자체만으로도 권한과 인증의 역할을 가질 수 있다.
JWT Token 의 구조
JWT Token 은 위에서 말했듯이 평범한 String 인데 어떻게 권한/인증을 체크할 수 있을까? JWT Token 자체를 보면 아래와 같이 마침표를 기준으롤 3가지 영역으로 나뉘어져 있다.
각자의 영역은 사진과 같이 각자의 영역이 있다. 아래의 사진은 위의 JWT Token 을 Decoding 했을 때의 결과다.
Header 영역
Header 영역에는 해당 Token 에 대한 유형
과 알고리즘에 대한 정보
가 포함되어 있다. 위의 Token 은 JWT 공식 홈페이지 에서 가져온 Token 으로서 Token 의 알고리즘은 HS256
이고, 유형은 JWT
이다. 알고리즘은 HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512 등 각 언어별로 지원하는 알고리즘이 있으니 공식 홈페이지에서 확인해서 선택하면 된다.
Payload 영역
Payload 영역은 기본적인 유저에 대한 정보와 함께 추가한 유저의 정보가 포험되어 있다. JWT Token 에서는 이미 정해져있는 이름을 가진 아래의 7개에 대한 정보를 담을 수 있다.
- iss: Token 발행자(Issuer) 으로서 해당 필드는 문자열 혹은 URI 로 이뤄지면 선택사항이다.
- sub: Token 의 제목 (Subject) 으로서 해당 필드의 값은 문자열 혹은 URI 로 이루어져 있다. 해당 값은 전역적으로나, 발행자 범위에서 유일한 값이어야 한다.
- aud: Token 의 대상자 (Audience) 로서 해당 값에 대한 인증이 이뤄지지 않으면 해당 Token 을 이용한 접근이 거부 된다. 해당 필드는 문자열 혹은 URI 로 이루어져 있다.
- exp: Token 의 유효한 날짜 정보(Expiration time) 으로서, 해당 Token 을 가지고 요청을 했을 때 현재의 시간을 지났다면 해당 Token 을 이용한 접근이 거부된다. 일반적으로는 몇 분 이내로 설정을 하며 해당 값은 숫자로만 이루어져야 한다.
- nbf: Token 의 유효 시작 날짜 정보 (Not before) 으로서, 해당 Token 을 이용하려면 현재의 시간이 이 정보에 기재된 시간과 같거나 지났어야 한다.
exp
와 마찬가지로 일반적으로는 몇 분 이내로 설정하며 해당 값은 숫자로만 이루어져야 한다. - iat: Token 의 발행된 날짜 정보(Issued At) 으로서, JWT 이 발행된 시간에 대한 정보를 담고 있다. 위의
exp
혹은nbf
와 마찬가지로 숫자로만 이뤄져야 한다. - jti: Token 의 고유 식별자(JWT ID) 로서, 해당 값은 고유한 식별자에 대한 값이 할당되어야 한다. 해당 값은 대소문자를 구별하는 문자열로 구성되어져 있다.
Signature 영역
Signature 영역은 각 Header, Payload 에 해당 하는 정보와 서명키에 대한 정보가 들어가져 있다. 이 영역은 Header 에서 지정한 알고리즘으로 만들어져 있으며, 인증에 대한 정보 확인 및 변조 여부를 확인하는데 사용된다.
JWT Token 의 특징
JWT Token 은 다음과 같은 특징이 있다.
JWT Token 은 Stateless 하다
기존의 웹 어플리케이션은 대체적으로 세션 기반으로 인증 처리를 많이 구현을 했다. 세션을 이용하면 단점이 어딘가에 그러한 세션 정보를 저장을 해야한다는 것이다. 그러한 정보는 대체적으로 서버의 메모리 에 저장을 하는 경우가 많다. 서버의 메모리에 저장을 하고 있다가 갑자기 유저의 수가 늘어난다면 여기에서 또 서버를 증설할 것이다. 그렇게 되면 문제점이 두가지가 생긴다. 첫번째는 늘어난 수만큼 서버의 메모리를 계속 사용을 해야한다는 점이 있을 것이고, 두번째는 세션 정보가 서버 간에 공유 되지 않는다는 점이다.
위의 사진과 같이 어떠한 유저가 A 서버로 접속해서 로그인을 했다고 가정해보겠다. 로그인을 하면 A 서버에는 해당 유저에 대한 세션 정보를 저장할 것이다.
그러다가 만약 유저가 B 서버로 접속이 됐다면 B 서버에는 세션 정보가 없어 로그인일 풀릴 것이다.(로드밸런싱에 대한 개념은 위 주제와는 벗어남으로 추후 따로 설명하도록 하겠다.) 그렇게 되면 로그인을 하고 나서도 계속 로그인을 하라고 나올 것이다. 실제 필자도 위의 경우에 직면한 적이 있었다. 물론 그러한 해결 방안으로는 Sticky session 을 이용했지만, 사실 근본적인 해결방법은 아니었다. (여기에서의 관점은 session 이 주제가 아님으로 따로 이야기하지 않고 넘어가도록 하겠다.) 물론 위와 같은 방법을 우회하는 방법도 있다. 자체 세션 서버를 따로 두는 것도 하나의 방법일 수 있다. 하지만 그렇게 몰아넣는 것 역시 서버에서는 부하가 걸릴 수 있다. 이와 같은 stateful 한 방법은 요즘과 같이 서버를 스케일 아웃(Scale-out)할 때 문제가 생길 수 있다. 이러한 stateful 과 상반되는 개념이 stateless 이다. JWT Token 은 서버 입장에서 요청을 받았을 때 그때그때 인증과 권한 체크를 하기 때문에 세션 처럼 따로 저장할 필요는 없다. 위와 같이 A 서버로 접속했다가 B 서버로 요청을 한다고 해도 문제 생길 것이 없다. 쉽게 이야기 해서 수평으로 쉽게 확장이 가능하다는 의미
가 된다.
JWT Token 은 무결성이 보장된다.
공식 홈페이지에 메인에서 테스트를 해보면 예시로 보여주고 있는 JWT Token 의 secret 키를 하나라도 입력을 하게 되면 JWT Token 에서 Signature 영역의 글자가 바로 바뀌는 것을 알 수 있다. 이처럼 JWT Token 은 변조가 되었을 때, 바로 알아차릴 수가 있다.