Cookie와 Session의 한계
기본적으로 HTTP는 stateless하기 때문에 종료된 연결에 대한 정보를 기억하지 않는다. 그래서 원칙적으로 따졌을 때 로그인이 필요한 기능을 이용하기 위해서는 매 요청마다 아이디, 비밀번호 등을 전송해줘서 인증 과정을 거쳐야 한다. 다행히 우리는 쿠키를 통해 요청 정보를 기억할 수 있게 되었다. 서버 측에서 클라이언트로 전송해준 쿠키는 클라이언트 측에 저장되어 클라이언트가 요청을 날릴 때마다 서버로 함께 보내진다. 서버 측은 이 쿠키를 통해 각 요청을 식별할 수 있다.
그러나 쿠키 역시 몇 가지 단점을 가진다. 아이디, 패스워드 등 민감한 정보가 포함된 쿠키는 클라이언트 측에 저장되기 때문에 노출될 위험이 있다. 또한 개수와 용량에 제한이 존재하며 브라우저끼리 공유도 불가능하다. 또한 조작이 가능하며 서버 측에서는 조작 여부를 알아챌 방법이 없다.
따라서 등장한 것이 세션이다. 세션을 사용하면 매번 쿠키에 아이디, 패스워드 등 민감 정보를 그대로 담아 전송하지 않고 인증 정보를 서버 측의 세션 저장소에 저장한다. 인증과 관련된 정보가 쿠키를 통해 클라이언트 측에 전달되며 이후 통신이 이루어질 때마다 서버 측은 세션 저장소에 담긴 정보와 쿠키에 담긴 정보를 비교하여 요청을 식별한다.
그렇지만 세션 역시 한계를 지닌다. 보안의 관점에서 바라봤을 때는 쿠키만을 사용하는 것보다 안전한 인증 시스템을 구축할 수 있지만 본질적으로 HTTP의 특성인 stateless가 위배된다. 그렇기 때문에 서버 리소스의 부담도 커지며 서버 측에 문제가 생기는 경우 모든 인증 시스템이 마비될 수도 있다. 또한 쿠키에 담긴 정보를 확인하기 위해 세션 저장소를 탐색하는 작업 역시 하나의 번거로움이다. (세션이 쿠키보다 느리다 등의 부수적인 이야기는 이번 포스트의 주제에서 벗어나므로 넘아가겠다.)
JWT (Json Web Token)
위와 같은 배경으로 인해 등장한 인터넷 표준 방식의 한 종류가 바로 JWT이다. JWT는 인증과 관련된 정보들을 Json 형태로 담은 토큰을 의미한다. JWT의 가장 큰 특징은 토큰에 서명이 존재한다는 것이다. 서버는 개인 키를 통해 서명을 확인함으로써 해당 토큰이 정상적인 토큰인지 여부를 확인할 수 있다.
JWT의 구성 요소
JWT는 헤더, 내용, 서명 총 세 가지 문자열로 구성되어 있으며 .를 통해 각 요소를 구분한다.
(1) 헤더 (Header)
헤더에는 토큰의 타입과 서명 검증을 위해 활용될 해싱 알고리즘이 들어간다. 토큰의 타입은 물론 "JWT"이다.
(2) 정보 (Payload)
payload에는 토큰 발급자, 토큰 대상자, 토큰 만료시간 등의 정보가 들어가며 표준 스펙 외에도 원하는 자유로운 정보를 삽입할 수 있다. JWT 토큰에서 header와 payload는 특별한 암호화 없이 Base64를 통한 인코딩만을 수행하기 때문에 그 내용을 쉽게 조회할 수 있다. 따라서 payload에는 민감성 정보가 포함되면 안 된다.
(3) 서명 (Signature)
header와 payload의 인코딩 값을 합친 후 서버의 개인키를 사용하여 해시한 값이 들어간다. 그렇기 때문에 토큰을 발급한 서버가 아니면 함부로 복호화할 수 없다.
JWT 토큰의 장점
(1) 서버 비밀키를 사용하여 해시 작업을 수행했기 때문에 보안이 강화된다. (유출 혹은 조작 가능성 감소)
(2) 서버 측에 별도의 인증 정보 저장소를 생성하지 않아도 되기 때문에 HTTP의 stateless 특성을 유지할 수 있다.