본문 바로가기

Spring

[Spring] 세션, 토큰, 쿠키

Cookie

쿠키를 이용해서 서버는 우리의 브라우저에 데이터를 넣을 수 있다.

보통 서버가 클라이언트에게 정보를 저장해두고 다음 요청 시 이를 식별하는 데 사용한다.

우리의 브라우저의 쿠키를 저장한 후 해당 웹사이트를 방문할 때마다 브라우저는 해당 쿠키도 요청과 함께 보내진다.

 

특징

  • 클라이언트의 웹 브라우저에 저장되는 작은 데이터 조각이다.
  • 쿠키는 클라이언트(브라우저)에 저장되며, 유효기간을 설정할 수 있다. 
  • 서버와 클라이언트의 모든 HTTP 요청마다 자동으로 전송된다.
  • 쿠키는 도메인에 따라 제한이 된다. (예를 들어, 구글이 준 쿠키는 구글에게만 보내지게 됨)
  • 쿠키는 인증 뿐만 아니라 여러가지 정보를 저장할 수 있다(언어설정을 바꾸면 서버는 쿠키를 주고, 선택한 언어를 저장).

 

쿠키와 보안 문제

  • 쿠키 값은 임의로 변경할 수 있다.
    • 클라이언트가 쿠키를 강제로 변경하면 다른 사용자가 된다.
    • 실제 웹브라우저 개발자모드 Application Cookie 변경하면 다른 사용자의 이름이 보임
  • 쿠키에 보관된 정보는 훔쳐갈 수 있다.
    • 만약 쿠키에 개인정보나, 신용카드 정보가 있다면?
    • 이 정보가 웹 브라우저에도 보관되고, 네트워크 요청마다 계속 클라이언트에서 서버로 전달된다.
    • 쿠키의 정보가 나의 로컬 PC에서 털릴 수도 있고, 네트워크 전송 구간에서 털릴 수도 있다.
  • 해커가 쿠키를 한번 훔쳐가면 평생 사용할 수 있다.
    • 해커가 쿠키를 훔쳐가서 그 쿠키로 악의적인 요청을 계속 시도할 수 있다.

대안

  • 쿠키에 중요한 값을 노출하지 않고, 사용자 별로 예측 불가능한 임의의 토큰(랜덤 값)을 노출하고, 서버에서 토큰과 사용자 id를 매핑해서 인식한다. 
  • 그리고 서버에서 토큰을 관리한다.
  • 토큰은 해커가 임의의 값을 넣어도 찾을 수 없도록 예상 불가능 해야 한다.
  • 해커가 토큰을 털어가도 시간이 지나면 사용할 수 없도록 서버에서 해당 토큰의 만료시간을 짧게(예: 30분) 유지한다. 
  • 또는 해킹이 의심되는 경우 서버에서 해당 토큰을 강제로 제거하면 된다.

토큰 (Token)

특정 자원에 대한 접근 권한을 표현하는 보안 문자열이다.

사용자의 인증 및 권한을 유지하고, 서버 간 API 호출에서 인증 수단으로 사용된다.

 

특징

  • 주로 액세스 토큰과 리프레시 토큰으로 나뉘며, 만료 기간을 설정할 수 있다.
  • 중앙 집중 관리가 아닌 방식으로, 자원 요청에 대한 인증을 제공한다.
  • 유효기간이 만료되면 무효화된다. 별도의 저장소가 필요하지 않다.

세션

서버에 저장되는 사용자별 상태 정보이다.

로그인 상태나 장바구니 같은 사용자별 데이터를 서버에 저장하고, 각 사용자에게 고유 세션 ID를 발급하여 클라이언트를 식별한다.

 

 

특징

  • 클라이언트는 세션 ID만 쿠키에 저장하고, 서버가 이 세션 ID를 바탕으로 사용자의 상태를 관리한다.
  • 세션 ID만 클라이언트에 저장하기 때문에 민감한 정보를 직접 노출하지 않는다.

JWT (JSON Web Token)

JSON 형식으로 데이터를 저장하고 서명하여 전달하는 토큰 기반 인증 방식이다.

세 부분(헤더, 페이로드, 서명)으로 구성되며, Base64 URL로 인코딩되어 전송된다.

사용자 인증, 권한 부여, 서버 간 데이터 전달 등에 사용된다.

 

특징

  • 서버에 상태 정보를 저장하지 않고, 클라이언트가 모든 정보를 포함한 토큰을 전달한다(Stateless).
  • 필요한 경우, 사용자의 정보와 권한이 인코딩된 상태로 전달되기 때문에 추가 요청 없이 권한을 확인할 수 있다.
  • 서명은 위조를 방지하지만, 민감한 정보는 인코딩이 아닌 암호화를 통해 보완해야 한다.

 

Refresh 토큰과 생명 주기

 

1. 로그인 성공시 생명주기와 활용도가 다른 토큰 2개 발급 : Access/Refresh

  • Access 토큰: 권한이 필요한 모든 요청 헤더에 사용될 JWT로 탈취 위험을 낮추기 위해 약 10분 정도의 짧은 생명주기를 가짐.
  • Refresh 토큰: Access 토큰이 만료되었을 때 재발급 받기 위한 용도로만 사용되며 약 24시간 이상의 긴 생명주기를 가진다.
  • 각 토큰은 각기 다른 생명주기, payload 정보를 가진다.

2. 권한이 필요한 모든 요청 : Access 토큰을 통해 요청

  • Access 토큰만 사용하여 요청하기 때문에 Refresh 토큰은 호출 및 전송을 빈도가 낮음

3. 토큰이 만료된 경우 Refresh 토큰으로 Access 토큰 발급

  • Access 토큰이 만료되었다는 요청이 돌아왔을 경우 “1”에서 발급 받은 Refresh 토큰을 가지고 서버의 특정 경로(Refresh 토큰을 받는 경로)에 요청을 보내어 Access 토큰을 재발급 받는다.

4. 서버측에서는 Refresh 토큰을 검증 후 Access 토큰을 새로 발급

  • 서버측에서는 Refresh 토큰을 받을 엔드포인트 (컨트롤러)를 구성하여 Refresh를 검증하고 Access를 응답

 

Access/Refresh 토큰 저장 위치

  • 로컬 스토리지 : XSS 공격에 취약함 : Access 토큰 저장
  • httpOnly 쿠키 : CSRF 공격에 취약함 : Refresh 토큰 저장

 

Refresh 토큰 rotate

  • Access 토큰이 만료되어 Refresh 토큰을 가지고 서버 특정 엔드포인트에 재발급을 진행하면 Refresh 토큰 또한 재발급하여 프론트측으로 응답하는 방식

 

로그아웃과 Refresh 토큰 주도권

  • 로그아웃을 구현하면 프론트측에 존재하는 Access/Refresh 토큰을 제거한다.
  • 그럼 프론트측에서 요청을 보낼 JWT가 없기 때문에 로그아웃이 되었다고 생각하지만 이미 해커가 JWT를 복제 했다면 요청이 수행된다.
  • 위와 같은 문제가 존재하는 이유는 단순하게 JWT를 발급해준 순간 서버측의 주도권은 없기 때문이다.
  • 위 문제의 해결법은 생명주기가 긴 Refresh 토큰은 발급과 함께 서버측 저장소에도 저장하여 요청이 올때마다 저장소에 존재하는지 확인하는 방법으로 서버측에서 주도권을 가질 수 있다.
  • 만약 로그아웃을 진행하거나 탈취에 의해 피해가 진행되는 경우 서버측 저장소에서 해당 JWT를 삭제하여 피해를 방어할 수 있다.
  • Refresh 토큰 블랙리스팅이라고도 부른다.

 

 

'Spring' 카테고리의 다른 글

[Spring] 예외 처리 방식  (0) 2024.12.11
[Spring] Security JWT  (0) 2024.11.10
[Spring] @Profile  (0) 2024.11.04
[Spring] 스프링 AOP - 실무 주의사항  (1) 2024.09.25
[Spring] 스프링 AOP - 로그 출력 AOP 만들기  (0) 2024.09.24