기본적으로 HTTP 프로토콜 환경은 Connectionless
, Stateless
한 특성을 가지기 때문에 서버는 클라이언트가 누구인지 매번 확인해야 하는데 이 특성을 보완하기 위해서 쿠키가 등장하게 되었다.
Stateless
상태의 프로토콜에서 상태기반 정보를 기억할 수 있는 쿠키가 개발되고 서버는 쿠키를 이용하여 사용자를 인증 할 수 있게 되었다.
쿠키는 서버가 사용자의 웹 브라우저에 전송하는 작은 데이터 조각이다. 브라우저는 그 데이터 조각들을 저장해 놓았다가, 동일한 서버에 재 요청 시 저장된 데이터를 함께 전송한다.
쿠키는 주로 세 가지 목적을 위해 사용된다.
서버에 저장해야 할 로그인, 장바구니, 게임 스코어 등의 정보 관리
사용자 선호, 테마 등의 세팅
사용자 행동을 기록하고 분석하는 용도
과거엔 클라이언트 측에 정보를 저장할 때 쿠키를 주로 사용했다. 쿠키를 사용하는게 데이터를 클라이언트 측에 저장할 수 있는 유일한 방법이었을 때는 이 방법이 타당했지만, 지금은 웹 스토리지 API (localStorage와 sessionStorage) 를 사용하면 된다.
HTTP 요청을 수신할 때, 서버는 응답과 함께 Set-Cookie
헤더를 전송할 수 있다.
이 서버 헤더는 클라이언트에게 쿠키를 저장하라고 전달한다.
TTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
이제, 서버로 전송되는 모든 요청과 함께, 브라우저는 Cookie 헤더를 사용하여 서버로 이전에 저장했던 모든 쿠키들을 회신 한다.
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
세션이란 일정시간 동안 같은 사용자로 부터 들어오는 일련의 요구를 하나의 상태로 보고 그 상태를 일정하게 유지시키는 기술이다.
세션은 중요한 정보를 클라이언트에 저장하는 쿠키와는 다르게 서버에 저장하여 관리하기 때문에 사용자 정보가 노출되지 않는다.
서버에서는 클라이언트를 구별하기 위해 각각의 세션ID를 클라이언트마다 부여하게 되며 클라이언트가 종료되기 전까지 유지한다.
이전에 쿠키만을 사용하여 인증을 했을 경우에는 클라이언트에서 사용자 정보를 관리하여 보안에 매우 취약하다는 단점이 있어 요즘에는 로그인과 같이 보안상 중요한 작업을 할 때는 세션을 사용한다.
예를들어 사용자가 로그인을 하면, 세션에 사용자 정보를 저장해두고 서비스를 제공할 때 사용하곤 한다. 이러한 서버 기반의 시스템은 다음과 같은 흐름을 갖는다.
이러한 인증 방식은 소규모 시스템에서는 아직 많이 사용되고 있지만, 웹/앱 어플리케이션이 발달하게 되면서 서버를 확장하기가 어렵다는 등 다음과 같은 문제점을 보이기 시작했다.
서버에서 클라이언트의 상태를 모두 유지하고 있어야 하므로, 클라이언트 수에 따른 메모리나 디스크 또는 DB에 부하가 심하다.
사용자가 늘어나게 되면 더 많은 트래픽을 처리하기 위해 서버를 확장해야 하는데 서버를 확장할때 세션이 저장되어 있는 서버로만 요청이 가도록 분산처리를 해줘야 하기 때문에 서버를 확장하기 어렵다.
웹 브라우저에서 세션 관리에 사용하는 쿠키는 단일 도메인 및 서브 도메인에서만 작동하도록 설계되어 CORS 방식(여러 도메인에 request를 보내는 브라우저)을 사용할 때 쿠키 및 세션 관리가 어렵다.
토큰 기반 인증 시스템은 인증받은 사용자들에게 토큰을 발급하고, 서버에 요청을 할 때 헤더에 토큰을 함께 보내도록 하여 유효성 검사를 한다. 이러한 시스템에서는 더이상 사용자의 인증 정보를 서버나 세션에 유지하지 않고 클라이언트 측에서 들어오는 요청만으로 작업을 처리한다.
즉, 서버 기반의 인증 시스템과 달리 상태를 유지하지 않으므로 stateless한 구조를 갖는다.
토큰은 클라이언트 측에 저장되기 때문에 서버는 완전히 Stateless하며, 클라이언트와 서버의 연결고리가 없기 때문에 확장하기에 매우 적합하다.
서버 기반 인증 시스템의 문제점 중 하나인 CORS를 해결할 수 있다. 토큰을 사용한다면 어떤 디바이스, 어떤 도메인에서도 토큰의 유효성 검사를 진행한 후에 요청을 처리할 수 있다
최근에는 Json 포맷을 이용하는 JWT(Json Web Token)을 주로 사용한다.
JWT(JSON Web Token)란 인증에 필요한 정보들을 암호화 시킨 토큰을 의미한다.
JWT는 .을 구분자로 나누어지는 세 가지 문자열의 조합이다.
alg과 typ는 각각 정보를 암호화할 해싱 알고리즘 및 토큰의 타입을 지정한다.
Payload는 토큰에 담을 정보를 지니고 있다. Key-value 형식으로 이루어진 한 쌍의 정보를 Claim이라고 칭한다.
Signature는 인코딩된 Header와 Payload를 더한 뒤 비밀키로 해싱하여 생성한다.
Header와 Payload는 단순히 인코딩된 값이기 때문에 제 3자가 복호화 및 조작할 수 있지만, Signature는 서버 측에서 관리하는 비밀키가 유출되지 않는 이상 복호화 할 수 없다.
따라서 Signature는 토큰의 위변조 여부를 확인하는데 사용한다.
토큰의 만료 시간을 짧게 설정하여 토큰이 탈취되더라도 빠르게 만료되기 때문에 피해를 최소화 할 수 있다. 그러나 사용자가 자주 로그인 해야 하는 불편함이 수반된다.
클라이언트가 로그인 요청을 보내면 서버는 Access Token 및 그보다 긴 만료 기간을 가진 Refresh Token을 발급하는 전략이다.
Access Token이 만료되었을 때 Refresh Token을 사용하여 Access Token의 재발급을 요청한다. 서버는 DB에 저장된 Refresh Token과 비교하여 유효한 경우 새로운 Access Token을 발급하고, 만료된 경우 사용자에게 로그인을 요구한다.
해당 전략을 사용하면 AccessToken의 만료 기한을 짧게 설정할 수 있으며, 사용자가 자주 로그인 할 필요도 없고 서버가 강제로 RefreshToken을 만료시킬 수 있다.
그러나 검증을 위해 서버는 RefreshToken을 별도로 저장하고 있어야 하므로 이는 JWT에 장점을 완벽하게 누릴 수 없다는 단점이 존재한다.