쿠키
클라이언트와 서버 간에 정보를 주고받는 수단이라고 보면 된다
사용자가 처음 웹 사이트에 접속할때 사이트로부터 쿠키를 받는다 (서버 → 클라이언트)
A 사이트에 로그인을 하면, A사이트의 DB에서 회원 정보를 확인 후 ‘회원 맞음’ 인증 정보를 담은 쿠키가 브라우저에 저장된다 (로그인 → 서버 DB 확인 → 사용자)
로그인 후 글을 쓰거나 댓글을 다는 등의 새로운 요청을 하거나 재접속하더라도 자동으로 브라우저에 저장된 쿠키가 서버로 전달되어 로그인 상태를 유지할 수있다
쿠키는 도메인에 종속 되는데, 무슨 말이냐면 A 도메인에서 발급된 쿠키는 A 도메인에만 사용할 수 있다
쿠키에 유효기간을 정해줄 수 있어, 만료된 후 자동으로 삭제할 수도 있다
예시로 은행처럼 보안상 일정시간 후에 세션이 만료되어 자동 로그아웃 되는 사이트들처럼 말이다
쿠키는 인증 뿐만 아니라 여러가지 정보를 저장할 수 있는데, 장바구니 정보나 언어 설정 같은 사용자 맞춤 데이터를 저장해두는 데도 많이 사용된다
보안 강화를 위해 HTTP-Only 속성을 부여할 수 있다 이 속성은 자바스크립트에서 쿠키에 접근하지 못하게 해 XSS 공격을 방지할 수 있다 토큰 방식에서 Access Token을 쿠키에 저장할 때 권장되는 속성이다
세션
HTTP 프로토콜은 stateless(상태를 기억하지 않는) 특성을 가진다
즉, 각 요청은 독립적으로 처리되며, 서버는 이전 요청의 상태를 기억하지 못하기 때문에
요청이 들어올때 마다 요청자가 누군지 확인해야 하므로 시간과 자원이 더 들게 되는데, 이를 보완하기 위해 세션을 사용한다
세션은 서버에서 관리하는 일시적인 저장 공간으로, 서버는 클라이언트로부터 받은 세션 ID를 기반으로 사용자 상태를 유지한다
사용자가 웹사이트를 방문하면 세션 ID가 담긴 쿠키가 브라우저에 저장되고, 이 쿠키를 통해 사용자는 이후 요청에서도 동일한 세션을 유지할 수 있다
서버는 이 세션 ID를 통해 사용자의 로그인 상태를 기억하거나 개인화된 데이터를 제공할 수 있다
다만, 세션은 주로 서버에 저장되며, 서버 리소스를 소모한다
사용자가 많아지면 서버에 더 많은 리소스를 필요로 할 수 있어, 이를 보완하기 위해 Redis 같은 메모리형 데이터베이스를 많이 사용한다고 한다
토큰
토큰이란 서명 같은 것인데, 마치 서류나 카드 뒷면에 서명을 하는것만으로 어떠한 계약에 효력이 생기는 것과 같이 인증된 사용자를 식별하는 디지털 서명의 역할을 한다
세션과 달리, 토큰은 DB 필요없이 서버에서 유저를 인증하는데 필요한 정보 (이를테면 세션 ID)가 담긴 토큰을 저장해서 주면 클라이언트에서 자체적으로 저장하고 이후 페이지를 요청할때 클라이언트가 그때 받은 토큰을 HTTP 요청의 헤더에 담아 서버로 보내면, 서버는 토큰이 유효한지만 검증하면 된다
때문에 토큰 기반 인증에서는 서버가 매번 DB를 확인하지 않아도 되므로, 트래픽이 많은 애플리케이션에서 효율적으로 사용할 수 있지만, 토큰이 유효한지 확인하려면 누구나 열어 볼 수 있어야 하기 때문에 아주 중요한 정보가 있으면 위험할 수 있다
예시로는 코로나때의 QR 코드 인증이 있다
JWT
JSON Web Token
토큰 방식의 인증에서 많이 사용되는 형태이다
인코딩 또는 암호화된 3가지 데이터를 이어 붙인 것으로, 세션 ID보다 훨씬 길고 괴상한 문자들로 이루어져 있음에도 공간제약이 없다 (쿠키는 공간제약이 있다)
잘보면 xxxxxxx.zzzzzz.yyyyy이런식으로 세부분으로 나뉘는데, 각각 1. 헤더 header, 2. 페이로드 payload, 3. 서명 verify signature 순으로 구성되어 있다
- 헤더 (Header): 토큰의 타입(JWT)과 서명에 사용할 알고리즘 정보(예: HS256)를 담고 있다
첫번째 헤더를 디코딩하면 두가지 정보가 담겨있는데,
- 토큰의 type : 타입은 항상 JWT로 고정값이다
- alg : 알고리즘의 약자로 3번 서명값을 만드는데 사용될 알고리즘이 지정된다
- HS256 등 암호화 방식 중 하나를 지정할 수 있다
- 페이로드 (Payload): 토큰에 담을 실제 데이터가 포함된다.(이렇게 되면 누구든 다시 디코딩을 통해 악용이 가능하므로 첫번째와 세번째 부분이 존재한다)
가운데 페이로드 부분을 base64로 디코딩해보면 JSON 형식으로 누가 누구에게 토큰을 발급했는지, 언제까지 유효한 토큰인지, 서비스가 사용자에게 토큰을 통해 공개하기 원하는 내용들 (사용자 ID, 권한 정보)이 있다
토큰에 담기는 이러한 데이터를 Claim 이라고 한다
- 서명 (Signature): 헤더와 페이로드를 합친 후, 서버에 감춰놓은 비밀 값까지 암호화 알고리즘에 넣고 돌리면 비로소 세번째 서명 값이 된다
즉, 탈취를 당하더라도 서버에 감춰놓은 비밀값을 알지 못하므로 소용없다
다만 세션 방식처럼 모든 사용자들을 기억하지 못하기 때문에 관리의 어려움이 있어 이를 보완하기 위해 토큰 기한을 짧게하는 방법이 있다
로그인을 하면 두개의 토큰이 주어지고 하나는 유효기간이 한시간이나 몇분 이하로 짧은 access 토큰, 하나는 그에 비해 꽤 길게 보통 2주 정도로 잡혀있는 refresh 토큰을 함께 사용한다
로그인시 서버는 리프레시 토큰의 상응값을 데이터베이스에 저장한다
사용자가 엑세스 토큰이 만료되면 리프레시 토큰을 서버로 보내고, 서버는 값을 대조하여 새로운 엑세스 토큰을 발행한다
리프레시 토큰만 안전하게 관리된다면 엑세스 토큰이 만료되어도 로그인 할 필요없이 클라이언트가 서버에 매번 세션을 요청하지 않고도, 엑세스 토큰을 발급 받아 자체적으로 인증 정보를 관리할 수 있다
하지만, 그사이 액세스 토큰을 탈취당한다면 즉시 무효화시키기 어렵다는 헛점은 존재한다
차이점
|
세션 |
JMT |
관리와 보안성 |
서버에 저장되어 있어 사용자 관리가 용이하다 특정 사용자의 세션을 강제로 만료시키거나, 계정을 차단할 수 있다 |
토큰 탈취 시 토큰의 유효기간 동안 차단할 방법이 없다 보안이 취약할 수 있다 |
서버 |
서버 리소스가 많이 소모된다 요청이 있을 때마다 세션을 관리하기위해 DB나 메모리에 접근해야 한다 |
사용자 정보를 저장할 필요가 없다 요청마다 DB에 접근하지 않아도 된다 네트워크 트래픽을 줄일 수 있다 |
장단점이기도 한 차이점을 표를 정리해봤다
세션은 요청이 있을때마다 DB를 탐색해야하고 ,사람이 많으면 DB 또한 더 필요하다
(이를 보완하기 위해 빠르고 저렴한 redis 메모리형 데이터베이스 서버를 많이 쓴다고 한다)
토큰은 DB를 매번 확인하지 않아도 되며 JWT은 공간차지도 안한다
참고 자료
https://www.youtube.com/watch?v=tosLBcAX1vk
https://www.youtube.com/watch?v=1QiOXWEbqYQ
https://youtu.be/aU4bju5kB_Q?si=CUpPENp_Px2hMkYf