쿠키 (Cookie)
정의
- 클라이언트(브라우저)에 저장되는 작은 데이터 조각
특징
- 브라우저에 '평문(문자열)'으로 저장되기 때문에, 개발자 도구를 통해 쉽게 확인할 수 있다
- 또한 'HttpOnly'를 적용하지 않으면 JS로도 접근 가능하고, HTTPS를 적용하지 않으면 네트워크 전송 중 탈취 위험이 존재한다
- 브라우저별로 크기 제한이 있고(약 4KB), 도메인당 쿠키의 갯수도 제한이 있다
- 만료 시간을 설정할 수 있고, 브라우저가 종료되어도 유지될 수 있다
동작
- 서버로 요청을 보낼때마다, 브라우저의 쿠키를 자동으로 함께 전송한다
종류
- 세션 쿠키(Session Cookie)
- 만료 시간(Expires/Max-Age)이 설정되지 않은 쿠키
- 브라우저 프로세스(브라우저 세션)가 끝나면 완전히 종료된다
- ex) JSESSIONID
- 영구 쿠키(Persistent Cookie)
- 특정 만료 시간(Expires/Max-Age)이 설정된 쿠키
- 브라우저 종료와 무관하게 만료 시점까지 유지되기에 영구 쿠키라 부른다
- 따라서 재접속시에도 쿠키 정보가 유지된다
세션 (Session)
정의
- '서버'에 저장되는 '사용자 상태' 정보
- 여기서 말하는 '사용자 상태' 정보는, 로그인 여부나 현재 컨텍스트처럼 세션 단위로 일시적으로 관리되는 정보를 말한다
- ex) userId, authenticated, loginTime, csrfToken, ...
- 즉, DB에 저장되는 유저 정보와는 다르다
특징
- 클라이언트에서는 세션ID만을 알 수 있고, 서버에 저장된 세션 정보는 알 수 없다
- 서버에 저장되기 때문에, 세션 크기나 개수는 브라우저가 아닌 서버 영향을 받는다
- 세션ID 쿠키 발급시 만료시간을 부여하지 않았다면, 브라우저 프로세스 종료 시 세션 ID가 삭제되어 접근할 수 없다
- 반면에 서버에 저장된 세션 자체는 타임아웃 전까지 유지된다
동작
- 로그인 직후 서버에서는 sessionId에 해당하는 session을, key:value 형태로 저장한다
- 클라이언트는 서버로부터 sessionId을 쿠키로 받고, 요청을 보낼때마다 자동으로 쿠키 헤더에 포함되어 전송된다
- 서버는 요청이 들어왔을 때, 헤더의 sessionId로 session을 찾고, 상태를 확인하여 인증/인가 처리를 수행한다
쿠키 vs 세션
차이
- 저장위치
- 쿠키 -> 클라이언트
- 세션 -> 서버
- 보안
- 세션이 상대적으로 안전하지만, 세션 또한 세션ID를 필수적으로 보호해야한다
- 크기/개수
- 쿠키 -> 브라우저 제한
- 세션 -> 서버 제한
- 타임아웃
- 쿠키 -> Expires/Max-Age
- 세션 -> session timeout
분산환경에서의 세션 관리
- Sticky Session (고정 세션)
- 정의
- 세션을 사용하는 방식
- 로드밸런서가, 특정 사용자의 요청을 항상 같은 서버로 보내는 방법
- 유저 상태가 저장된 서버로 해당 유저의 요청을 보내므로, 분산환경에서도 세션을 사용할 수 있다
- 특징
- 구현이 간단하지만, 특정 서버에 부하가 집중될 수 있다
- 세션 데이터에 대한 빠른 로컬 접근이 장점인 방식이므로, 외부 저장소가 아닌 서버 메모리에 Session 저장이 권장된다
- 따라서 서버 장애시 세션이 손실될 위험이 있다
- 정의
- Shared Session Storage (공유 세션 저장소)
- 정의
- 세션을 사용하는 방식
- '공유 세션 저장소'를 통해, 여러 서버가 세션을 공유하는 방법이다
- 특징
- Redis나 MySQL처럼 HttpSession 저장을 지원하는 기타 외부 저장소를 사용한다
- 외부 저장소에 모든 HttpSession을 저장하여 공통적으로 다룸으로써, 분산환경에서 세션을 한번에 관리할 수 있다
- SPOF의 위험성이 존재하기에, Clustering이나 Replication 등을 통해 고가용성을 확보해야한다
- 정의
- JSON Web Tokens (JWT)
- 정의
- 세션 대신 'JWT'를 사용하는 방식
- JWT는 'header.payload.signature' 구조로 되어있다
- 'header'와 'payload'는 BASE64URL로 인코딩 되어있다
- 특징
- 요청에 필요한 유저 인증 정보는 JWT에 보관하고, JWT는 브라우저의 localStorage나 cookie 등에 선택하여 저장한다
- JWT는 '암호화'가 아니라 '인코딩' 되어있다
- 따라서 'payload'에 사용자가 저장한 claims를 누구나 디코딩해서 확인할 수 있으므로, 민감한 정보를 넣으면 안된다
- 서버로 요청이 들어오면 Header의 JWT를 서버의 secretKey로 검증하여, 전송된 JWT의 유효성을 확인하고 JWT에 저장된 정보를 확인한다
- 정의
Stateless
정의
- HTTP의 특성중 하나로, 다음 조건을 만족하면 Stateless 하다고 말한다
- 서버가 클라이언트의 이전 요청에 대한 '어떤 정보'도 기억하지 않으며
- 각 요청은 서버가 요청을 처리하는데 필요한 '모든 정보'를 포함해야 한다
장점
- 각 요청이 독립적이다 -> 분산 환경에서 로드밸런서가 요청을 어떤 서버로 보내든 동일한 응답이 보장된다
- 다른 서버에서도 동일한 응답 -> 하나의 서버 장애시 다른 서버가 즉시 동일한 역할을 수행할 수 있으므로 고가용성이 보장된다
- 동일한 요청이면 동일한 응답 -> 캐싱에 유리하다
- 따라서 '확장'에 유리하다
단점
- 매 요청마다 인증 정보를 포함해야한다 -> 네트워크 오버헤드가 증가할 수 있다
- 연속적인 작업 흐름을 처리하기 위해, 각 요청마다 필요한 상태와 정보를 모두 포함해야한다 -> 복잡한 상호작용에서 구현과 설계가 어렵다
- 분산환경에서 트랜잭션 구현 난이도가 높다
세션과 Stateless
문제
- 세션은 Stateless하지 않은데, 그렇다면 이는 HTTP 통신에서 적절하지 않은 방식인가?
결론
- 서버에 저장된 session에서 사용자의 상태를 관리하고 있으므로, Stateless 하지 않은 것은 맞다
- 하지만 사용자의 인증과 상태 관리는 필수적인 부분이다
- 따라서 실용적인 측면으로 접근한다면, 확장 가능성이 낮거나 초기 단계에서 좀 더 보안적으로 안전한 session을 사용하는 것도 적절한 방법이다