이번에node.js
기반 Vue.js
로 웹 프론트엔드 개발을 맡게 되었는데 로그인 기능을 구현하면서 혼동되는 세션
, 쿠키
, 토큰
의 개념을 정확하게 짚고 넘어가고 싶었다.
서버 인증이 필요한 이유
현재 앱/웹 서비스에서 가장 많이 쓰이는 통신 방식은 HTTP
이다.
클라이언트는 서버에게 GET
, POST
, PUT
, DELETE
방식 중 하나로 요청을 보내게되고 요청 받은 자원에 대해 응답을 하게 된다.
그런데 여기서 문제는 통신에 대한 기록이 남지 않는다는 것이다. 즉, 서버는 사용자가 누군지 모른다.
그 이유는 HTTP
는 무상태 프로토콜(Stateless Protocol)
이기 때문이다.
무상태 프로토콜을 간단하게 말하면 통신 파트너에 대한 세션 정보나 상태 보관을 요구(저장)하지 않는다는 것이다.
인증이 필요없는 웹/앱이라면 상관없지만 사용자 정보에 따라 컨텐츠를 제공하거나 달라져야하는 경우에는 서버는 사용자가 누군지 알아야한다.
따라서, 사용자 정보
를 알릴 수 있는 방법이 세션
, 쿠키
, 토큰
이다.
쿠키(Cookie)란
- 클라이언트 로컬에
key-value
형태로 저장되어있는 파일이다. - 클라이언트 상태 정보를 브라우저에 저장하여 참조한다.
- 이름, 값, 유효 시간, 경로 등을 포함하고 있다.
구성요소
- 쿠키의 이름
- 쿠키의 값
- 쿠키의 유효시간
- 쿠키를 전송할 도메인 이름
- 쿠키를 전송할 경로
- 보안 연결 여부
- HttpOnly 여부
특징
- 쿠키 시간을 지정할 수 있다
- 브라우저가 종료되어도 유지된다
- 최대 저장할 수 있는 쿠키는 300개, 도메인당 20개, 쿠키 하나당 4096bytes(4Kb) 이
- 요청 시 자동으로 헤더에 넣어서 보냄
- 응답 헤더에 Set-Cookie 속성을 사용하면 쿠키를 생성할 수 있다
동작방식
이미지 출처 : https://interconnection.tistory.com/74
- 클라이언트가 서버에 요청을 보낸다.
- 상태를 유지하고 싶은 정보를 쿠키로 생성한다.
(Set-Cookie)
- 전달받은 쿠키는 브라우저 로컬에 저장/관리하고 있다가, 다음 요청 때 쿠키를 헤더에 넣어서 보낸다.
- 서버는 쿠키 정보를 받아 이전 상태 정보를 확인하여 응답한다.
쿠키 사용 예시
- 아이디/비밀번호 저장
- 쇼핑몰 장바구니
세션(Session)이란
- 일정 시간 동안 같은 브라우저로 들어오는 요청을
하나의 상태
로 보고 그 상태를 유지하는 기술이다. - 웹 브라우저를 통해 서버에 접속한 이후부터 종료하는 시점까지 유지되는 상태이다.
- 세션도 쿠키를 사용하여
key-value
을 주고 받으며 통신한다.
동작방식
이미지 출처: https://doooyeon.github.io/2018/09/10/cookie-and-session.html
- 클라이언트가 서버에 요청
- 서버는 요청한 클라이언트에게 유일한
세션ID
를 부여한다. - 서버는 응답 헤더
(Set-Cookie)
에세션ID
를 넣어서 전송한다. - 클라이언트는 이후 웹 브라우저를 종료하기 전까지 다음 요청부터는 부여된
세션ID
를 요청헤더에 넣어서 전송한다. - 서버는
세션ID
를 확인하고, 해당 세션에 대한 정보를 확인하여 응답한다.
세션 사용 예시
- 로그인
쿠키(Cookie) vs 세션(Session)
저장위치 :
- 쿠키 : 클라이언트(브라우저)
- 세션 : 서버
보안 :
- 쿠키 : 클라이언트에 저장되므로 보안에 취약
- 세션 : 쿠키를 이용해 Session ID만 저장하고 이 값으로 구분해서 서버에서 처리하므로 비교적 보안성이 좋다.
라이프사이클
- 쿠키 : 만료시간에 따라 브라우저를 종료해도 계속해서 남아 있을 수 있다.
- 세션 : 만료시간을 정할 수 있지만 브라우저가 종료되면 만료시간에 상관없이 삭제된다.
속도
- 쿠키 : 클라이언트에 저장되어서 서버에 요청 시 빠르다.
- 세션 : 실제 저장된 정보가 서버에 있으므로 서버의 처리가 필요해 쿠키보다 느리다.
세션(Session)의 한계
참고 블로그 : [Web]쿠키, 세션 그리고 토큰 참고 블로그 : [인증/인가]Session(세션)과 Token(토큰)(JWT)의 차이점
한계 1: 안정성
세션
은 서버에서 저장/관리하기 때문에 상대적으로 온전한 상태를 유지하기 유리하다.
하지만 공격에 대한 취약점이 존재하기 때문에 유효기간, HttpOnly, Secure 옵션 등을 주어 쿠키에 저장한다.
토큰
은 반대로 웹 브라우저(로컬스토리지, 쿠키 등)에 저장되기 때문에 공격에 노출될 가능성이 더 크다.
이를 대비해 토큰
은 민감한 정보를 담지 않는다. 그리고 유효기간도 짧게 설정하여 공격에 대한 노출시간을 최소화한다.
하지만 짧은 유효기간으로 토큰이 무효화되면 사용자는 계속해서 토큰을 요청해야하는 번거로움이 있기 때문에 애초에 로그인(인증)시 refresh token이라는 것을 추가적으로 발급한다.
refresh token은 좀 더 긴 유효기간을 가졌으며 최대한 안전한 곳에 저장된다. 기존 토큰이 만료되거나 변질되면 refresh token을 통해 재발급한다.
한계 2: 저장소 부하
세션은 서버에 저장하기 때문에 클라이언트가 많으면 많을수록 많은 세션정보를 서버에서 감당해야 한다. 세션 전용 서버를 만들어 DB에 저장해도 DB를 부하시키는 건 똑같다.
한계 3 : 확장성
한계 2
의 문제를 해결하기 위해 서버를 여러 대를 뒀다고 가정해보자.
서버가 스케일아웃되서 여러 개가 생기면 각 서버마다 세션 정보를 저장하게 된다.
이렇게 되면 세션정보가 없는 다른 서버에 접속할 때마다 계속 로그인해줘야 한다.
한계 4 : 웹/앱 간 상이한 세션 처리 로직
모바일이 나오기 전에는 클라이언트는 웹이 유일했다. 하지만 모바일이 생기면서 앱이 생겼고 이를 처리하는 경우도 생각해야한다. 웹과 앱의 쿠키 처리 방법이 다르고, 만약에 다른 클라이언트가 생긴다면 이에 맞는 처리 로직을 또 생각해야한다.
Token의 등장
세션의 한게를 해결하기 위해 등장한게 Token
방식이다.
토큰은 서버에 상태를 저장하지 않으며 토큰 자체로 정보를 담고있기 때문에 별도의 인증서버를 둘 필요가 없다. 따라서 요청 받을 서버 자체에서 인증 프로세스를 수행할 수 있다.
또한 토큰은 Json
포맷으로 통신하기 때문에 어떤 클라이언트(웹/앱)
에서 통신을 보내도 Json
포맷만 맞춰서 이용하면 토큰을 사용할 수 있다.
토큰은 세션과 달리 유효시간을 짧게 설정하여 공격에 노출되는 시간을 최소화 한다고 설명했다. 그리고 민감한 정보는 담지 않기 때문에 세션보다 비교적 안전
하다
유효시간이 끝나면 새로운 Access Token
을 발급받는 방식을 사용한다.
[Access Token] VS [Refresh Token]
Access Token(JWT)
는 제 3자에게 탈취당할 경우 보안에 취약하다는 단점이 있다.
이를 해결하기 위해 Access Token
의 유효기간을 짧게 설정하는데 사용자는 만료될 때마다 로그인을 해서 Token
을 재발급 해야하는 번거로움이 있다.
그래서 등장한게 Refresh Token
이다.
Refresh Token
은 Access Token
와 같은 JWT
이지만 처음 로그인을 완료 했을 때 Access Token
과 동시에 발급된다.
Refresh Token
은 긴 유효기간을 가지면서 Access Token
가 만료 됐을 대 재발급해주는 열쇠 역할을 한다.
예를 들어, Access Token
은 유효기간이 30분, Refresh Token
은 유효기간이 2주라고 가정하고, 사용자는 로그인을 하고 Access Token
과 Refresh Token
을 받는다.
클라이언트는 Refresh Token
을 안전한 곳(Local Storage?) 에 저장하고, API
요청은 Access Token
을 가지고 할 수 있다.
30분이 지나 토큰이 만료되면 Refresh Token
을 서버에 보냄으로써 새로운 Access Token
을 발급받을 수 있게 되고, 발급받은 Access Token
으로 다시 API요청을 할 수 있게 된다.