개요
저는 이전까지 Spring Security을 사용하여 jwt로 인증을 진행하였습니다. Json 로그인이 성공하면 accessToken과 refreshToken을 프론트에 발급해 주었으며, 서버에 접근하기 위해서는 헤더에 토큰 정보를 담아 요청하면 인증이 성공하여 Api로 원하는 동작을 수행하도록 구현하였습니다.
헤더에 토큰값을 넣어 인증하는 가장 간단하고 직관적인 방식이지만, 이번에 쿠키와 세션에 대해 공부하고 프론트와 협업해보며, 보다 안전하고 효율적인 로그인 방식을 구성해보기로 하였습니다.
이번 글은 스프링 시큐리티와 jwt를 활용한 인증 로직이 모두 구현 되어 있다는 전제 하에, 프로젝트를 진행하며 프론트와 협업시 쿠키와 세션, jwt를 어떻게 적절히 활용하면 좋을지에 대해 작성한 글입니다. 만약 Security와 jwt가 구현이 안되었거나 기초 개념이 헷갈리시는 분들은 먼저 아래 제가 벨로그에 작성한 시큐리티 시리즈를 참고하고 오시면 됩니다. 개념부터 구현까지 모두 작성되어 있습니다.
배경 지식
우선, 효율적인 인증 방법을 고안하기 위해서는 로그인은 어떤 방식으로 이루어 지는지, 쿠키와 세션, HTTP, JWT에 대한 기초적인 지식이 선행되어야 합니다. 하나하나 살펴보겠습니다.
JWT
JWT에 대해 제가 시큐리티 로그인 구현중 설명해놓은 글이 있습니다. 아래 글을 참고하시면 충분히 이해할 수 있을거라고 생각합니다.
클라이언트 서버 구조
클라이언트와 서버의 구조는 Request - Response 구조입니다. 클라이언트는 서버에 요청을 보내고, 응답을 대기하는 구조입니다. 이때 서버는 요청에 대한 결과를 만들어서 응답합니다. 이때 요청과 응답을 Http 프로토콜을 이용해 주고 받습니다.
HTTP 프로토콜의 특징
Connectionless (비연결성)
- 서버는 클라이언트의 상태를 보관하지 않고, 요청에 대한 응답만을 전송합니다
- 모든 클라이언트에 대해 각각 연결된 상태로 둔다면, 서버 비용이 기하급수적으로 증가하게 됩니다.
- 이러한 비용을 줄이기 위해 서버는 하나의 요청에 대해 응답을 보내고 바로 연결을 끊습니다.
- 1시간 동안 수천명이 서비스를 사용해도 실제 서버에서 동시에 처리하는 요청은 수십개 이하로 매우 작음
Stateless (무상태)
- Http 통신은 연결을 끊는 순간 서버와 클라이언트 간의 통신이 종료되며, 상태 정보를 유지하지 않습니다.
- 첫 번째 통신이 끝나고, 두번째 통신에서는 이전 통신에 대한 정보가 전혀 없습니다.
- 서버의 확장성은 높지만(스케일 아웃), 클라이언트의 상태를 기억하지 않기에 추가적으로 데이터를 전송해야 한다는 단점이 있습니다.
- 실제로는, 이전의 데이터를 유지해야 하는 경우가 많습니다.
이러한 특성때문에, 서버와 클라이언트가 통신을 할 때 통신이 연속적으로 이어지지 않고 한 번 통신이 되면 끊어지게 됩니다. 통신이 끊어지면 상태정보가 유지되지 않기 때문에 매번 페이지를 이동할 때마다 로그인을 다시 해야 하거나, 상품 선택 후 구매 페이지에서 선택한 상품의 정보가 없거나 하는 등의 문제가 발생할 수 있습니다.
HTTP 프로토콜의 번거로움과 불편함을 해결하기 위해 사용하는것이 바로 세션과 쿠키 입니다.
🍪 쿠키
HTTP의 일종으로 사용자가 어떤 웹 사이트를 방문할 경우, 해당 사이트가 사용하고 있는 서버에서 사용자의 컴퓨터에 저장하는 작은 기록 정보 파일입니다.
HTTP에서 클라이언트의 상태정보를 쿠키 형태로 클라이언트 PC에 저장하였다가 필요 시 정보를 참조하거나 재사용할 수 있습니다.
쿠키의 특징
- Key-Value 쌍으로 구성된 데이터입니다.
- 구성요소
- name : 쿠키를 식별하는 키 (중복 X)
- value : 쿠키의 값
- expires : 쿠키의 만료기한
- max-age : 쿠키의 만료 시간(초)
- domain : 쿠키가 저장된 도메인
- path : 쿠키가 사용되는 경로
- secure : secure 옵션 적용시, https인 경우에만 전송
- httpOnly : XSS 공격 방지용. JS에서 접근 불가, Http인 경우에만 전송
- sameSite : XSRF 공격 방지용. 요청 도메인과 쿠키 도메인이 같은 경우에만 쿠키 전송
- 클라이언트에 총 300개의 쿠키를 저장할 수 있습니다.
- 도메인당 20개의 쿠키를 가질 수 있습니다.
- 하나의 쿠키는 4KB(= 4096 byte)까지 저장이 가능합니다.
쿠키의 사용 목적
- 세션 관리 (Session ManageMent) : 로그인, 사용자 정보, 장바구니, 접속 시간 등 서버가 알아야 할 정보 저장
- 개인화 (Personalization) : 사용자에게 최적화된 페이지 및 정보 노출
- 트래킹 (Tracking) : 사용자의 행동 및 패턴을 분석 및 기록
쿠키의 동작 방식
- 클라이언트가 ID, PW를 통해 서버에 로그인을 요청
- 서버는 사용자를 식별할 정보(ID 또는 ID,PW쌍)를 쿠키에 담아 응답 전송
- 클라이언트는 서버에 요청시 쿠키를 함께 전달
- 서버는 쿠키에 있는 정보를 확인해 로그인 여부를 확인하여 클라이언트에 응답
하지만 이 방식에는 심각한 보안 문제가 있습니다. 쿠키의 값은 임의로 변경될 수 있기 때문에, 클라이언트에서 강제로 다른 사용자로 변장할 수 있습니다. 또한 쿠키는 네트워크 전송 구간이나 로컬에서 탈취당할 수 있기 때문에, 중요한 정보(결제 정보 및 비밀번호 등)가 쿠키안에 있다면 큰 위협이 되며, 해커가 탈취한 쿠키는 악의적인 요청에 계속 사용될 수 있습니다.
이 문제를 해결하기 위해 다음과 같은 대안을 사용할 수 있습니다.
- 쿠키에 중요한 값을 포함하지 않고, 사용자 별로 예측 불가능한 임의의 랜던 값(토큰)을 노출하고, 서버에서 토큰과 ID를 매핑하여 인식하도록 합니다. 이때, 토큰의 관리 주체는 서버여야 합니다.
- 토큰은 해커가 임의의 값을 넣어도 찾을 수 없도록 암호화 되어야 합니다.
- 해커가 토큰을 탈취하더라도 평생 사용할 수 없게 해당 토큰의 유효기간을 짧게 설정하여야 합니다.
세션
일정 시간 동안 같은 사용자(브라우저)로부터 들어오는 일련의 요구를 하나의 상태로 보고, 그 상태를 유지시키는 기술입니다.
여기서 일정 시간은 방문자가 웹 브라우저를 통해 웹 서버에 접속한 시점부터 웹 브라우저를 종료하여 연결을 끝내는 시점을 말합니다.
즉, 브라우저가 종료되기 전까지 클라이언트의 요청을 유지하게 해주는 기술을 세션이라고 합니다.
세션의 특징
- 웹 서버에 웹 컨테이너의 상태를 유지하기 위한 정보를 저장
- 웹 서버에 저장되는 쿠키(세션 쿠키 / session cookie)이다.
- 브라우저를 닫거나, 서버에서 세션을 삭제했을 때만 삭제가 되기 때문에 쿠키보다 비교적 보안적으로 우수
- 저장 데이터에 제한이 없다.(서버 용량 허용 범위 내)
- 각 클라이언트에 고유 세션 ID(Session ID)를 부여한다. 세션 ID를 통해 클라이언트를 구분하여 각 요구에 맞는 서비스를 제공
세션의 동작 방식
유니크한 세션 아이디를 쿠키에 담아 통신하는 방식입니다. 로그인이 성공한 후, 생성된 세션 ID가 세션 저장소에 있다면 ID를 쿠키에 담아 보내고, 없다면 새로운 세션 ID를 생성하여 쿠키에 담아 보냅니다.
세션의 사용 목적
사용자의 중요한 개인정보를 외부에 노출하지 않고, 서버에서만 다루기 위해 사용합니다.
쿠키 vs 세션
쿠키와 세션의 차이점을 한 눈에 정리해보도록 하겠습니다.
Cookie | Session | |
저장 위치 | Client | Server |
저장 형식 | Text | Object |
만료 시점 | 쿠키 저장 시 설정 (지정하지 않으면 종료시) | 정확한 시점 X |
리소스 | 클라이언트 리소스 소비 | 서버 리소스 소비 |
속도 | 쿠키에 정보가 담겨있어 비교적 빠름 | 정보가 서버에 있어 비교적 느림 |
세션과 쿠키를 하나만 사용하지 않고, 세션을 쿠키에 담아 사용하는 이유는, 세션이 쿠키에 비해 보안이 높은 편이나 세션은 서버에 저장되고, 서버의 자원을 사용하기 때문에 서버 자원에 한계가 있고, 속도가 느려질 수 있기에 쿠키를 같이 사용합니다.
자원관리 차원에서 쿠키와 세션을 적절한 요소 및 기능에 병행 사용하여 서버 자원의 낭비를 방지하며 웹사이트의 속도를 높일 수 있도록 해야 합니다.
마치며
이번 포스팅에서는 쿠키와 세션에 대해 알아보았습니다. 지금까지 공부한 내용을 바탕으로 생각한 로직은, 세션 ID라는건 결국 유저를 식별하기 위함이기 때문에 세션 아이디 대신, 서버에서 자체적으로 발급해주는 JWT 토큰을 쿠키에 설정해두어, 쿠키와 JWT인증 방식을 혼용하여 사용하면 될것 같다는 생각이 들었습니다.
그렇게 되면 매번 로그인 해야하는 수고도 덜고, 쿠키도 사용하며 보다 안전한 환경을 구축할 수 있겠다는 생각이 듭니다. 하지만, accessToken을 쿠키에 설정하여 그대로 사용하기에는 탈취당할 수 있는 문제점이 존재합니다.
따라서, 다음 포스팅에서는 다양한 보안 위협에 대해 알아보고, 어떤 방식이 가장 제가 진행하는 프로젝트에 적합할지 좀 더 고민을 거쳐 방식을 정하도록 하겠습니다. 이후에는 구체적인 구현과정과 코드를 함께 포스팅 하려고 합니다.
공부하며 작성한 내용이기 때문에, 혹시라도 잘못된 내용이 있으면 피드백 부탁드립니다. 다음 포스팅에서 뵙겠습니다.
참고
- https://velog.io/@octo__/%EC%BF%A0%ED%82%A4Cookie-%EC%84%B8%EC%85%98Session#%EC%84%B8%EC%85%98sessionhttps://velog.io/@kimdy0915/%EC%9D%B8%EC%A6%9D-%EB%B0%A9%EC%8B%9D%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98-JWT%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90#%EC%BF%A0%ED%82%A4cookie
- https://velog.io/@kimdy0915/%EC%9D%B8%EC%A6%9D-%EB%B0%A9%EC%8B%9D%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98-JWT%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90#%EC%BF%A0%ED%82%A4cookie
- 김영한님의 Http기초 강의