가끔 생각을 해요 ʕتʔ

☑️ 문제: Access Token 및 Refresh Token 관리에 대한 혼란

  • 프로젝트 시 처음으로 유저 인증 구현 부분을 맡았었는데, 초기에 리프레시 토큰을 새로운 액세스 토큰 자체로 오해하였다.
  • 작성한 코드에서 액세스 토큰을 클라이언트에 노출해 보안 취약성이 발견되었다.

☑️ 해결방법

  • 액세스 토큰 및 리프레시 토큰의 목적 및 용도를 먼저 이해하였다.
  • 토큰 저장 방법과 전달 및 갱신 방법에 대한 탐색을 하였다.(유저 정보를 저장하는 방식? / 토큰을 어떻게 저장할 것인가? / 클라이언트에게 액세스 토큰을 직접 노출시키지 않고 안전하게 제공하는 방법이 무엇일까? / 액세스 토큰 만료 시 어떻게 새로운 액세스 토큰을 생성할 것인가? / 리프레시 토큰도 만료되면 어떻게 할 것인가?)
  • 토큰 갱신 흐름을 설계하여 토큰 관리에 대한 체계를 구축하고, 코드로 작성하기 편하도록 하였다.

 

해결방법에 대한 자세한 내용은 아래에 정리되어 있다.

 

🍒 Access Token과 Refresh Token

 

Access Token의 목적과 한계

액세스 토큰이란 사용자가 리소스에 접근할 때 권한을 인증하기 위해 사용되는 토큰이다.

주로 웹 애플리케이션에서 사용자가 로그인하고 세션을 유지하는 동안에만 액세스 토큰이 유효하다.

목적

  • 권한 부여
    사용자가 인증된 상태에서 서비스나 리소스에 접근할 수 있는 권한을 부여한다.
  • 보안 강화
    서비스 접근 시 패스워드 등을 매번 요구하지 않고 토큰을 사용하여 인증을 간편하게 처리할 수 있다.

한계

  • 짧은 수명
    일반적으로 액세스 토큰은 보안을 위해서 짧은 유효기간으로 지정한다.(보통 1시간 내외)
    유효기간이 짧은 이유는 만약 액세스 토큰이 탈취되었을 경우 해킹자가 유효 기간 동안 리소스에 자유롭게 접근할 수 있기 때문에 보안적인 문제를 방지하기 위함이다. 따라서 수명이 짧아 만료될 때마다 로그인을 하는 등 새로 인증을 해야하는 문제가 있다.

 

Refresh Token이 필요한 이유

리프레시 토큰이란 보통 액세스 토큰과 함께 발급되며, 액세스 토큰의 유효기간이 만료된 경우 갱신을 위한 목적으로 사용된다.

목적

  • 액세스 토큰 갱신
    액세스 토큰이 만료되었을 때, 리프레시 토큰을 사용하여 새로운 액세스 토큰을 발급받을 수 있다.

필요한 이유

  • 장기간 인증
    사용자는 액세스 토큰이 만료되었을 때 추가적인 로그인 과정 없이도 계속해서 서비스를 이용할 수 있다.
  • 긴 수명
    리프레시 토큰은 일반적으로 액세스 토큰보다 긴 유효기간을 가진다.(보통 2주)
    사용자는 리프레시 토큰이 만료되었을 때 새로 인증하게 된다.

 

 

🍒 유저 정보 저장 방법

 

유저의 정보를 저장하는 방법은 아래와 같이 선택지가 많다. 완벽하게 안전한 저장 방법이란 없고 계속 고민해봐야할 부분이지만, 최대한 보안적으로 안전하다고 판단되는 방식을 선택하였다.

세션

  • 유저 정보를 서버의 메모리에 저장한다. 보통 세션 ID를 쿠키로 전달받아 세션DB에서 해당 유저 정보를 조회하여 사용한다.
  • 단점은 유저의 수가 증가하면 많은 유저의 세션 정보를 메모리에 저장해야 하므로, 서버 부하가 클 수 있다.

쿠키

  • 서버에서 키와 값을 설정하여 브라우저에 저장하고, 해당 도메인에 요청을 보낼 때마다 헤더에 쿠키를 포함시킨다.
  • http-only 옵션을 설정해서 JS로부터 쿠키에 접근할 수 없도록 설정해주어야 XSS 공격으로부터 안전하다.
  • 단점은 암호화가 되어있지 않고, 데이터 저장 용량이 적으며, 매번 요청 시마다 쿠키가 전송되어 트래픽이 증가할 수 있다.

Token

  • 쿠키를 사용할 수 없는 환경에서 사용한다. 토큰을 사용해 세션DB에서 유저 정보를 찾는다.
  • 단점은 토큰을 검증하고 나서 유저 정보를 찾는 과정이 추가되므로 서버 부하가 높아질 수 있고, 클라이언트에 저장되므로 XSS 공격에 취약할 수 있다.

Web storage

  • 클라이언트에 데이터를 저장할 수 있게 HTML5부터 나온 새로운 방식의 데이터 저장소를 말한다. 키와 값의 형태로 데이터를 저장한다.
  • web storage 또한 클라이언트에 저장되므로 보안에 취약할 수 있다. 서버에 요청을 보내지 않으므로 서버 부하는 감소하지만, 클라이언트 측에서 데이터를 다룰 때 성능 저하가 발생될 수 있다.

 

  1. 로컬스토리지
    • 클라이언트 기기에 데이터를 영구적으로 저장한다. 직접 삭제하지 않는 한 영구적으로 남아 있다.
  2. 세션스토리지
    • page session을 의미한다. 창이 닫히면 삭제되는 휘발성 데이터를 저장한다.

JWT

  • 서버에 정보를 저장하지 않고도 로그인 상태를 유지할 수 있게 해준다.
  • 사인 알고리즘을 사용하여 유저에게 사인을 보내고, 요청 시 사인을 확인하여 올바른 요청인지 확인한다.
  • 단점은 요청이 많아질수록 서버 부하가 커질 수 있다.

 

 

이 중에서 내가 선택한 방법은 JWT 토큰에 정보를 저장하고, http-only 쿠키를 통해 클라이언트에 전달하는 방식을 선택했다. JWT의 편리함과 쿠키의 보안성을 결합하여, 사용자 정보를 안전하게 전달할 수 있기 때문이다. 일반적으로 JWT 토큰은 클라이언트에 저장되며, 클라이언트에서 토큰을 수정하거나 조작할 수 있는데 그 부분이 보안 취약점이 될 수 있다. 따라서 보안을 강화하기 위해 토큰을 클라이언트에 직접 저장하는 대신, http-only 옵션을 설정한 쿠키를 사용해서 토큰을 안전하게 저장하고 전달하였다.(secure, samesite 옵션도 고려해볼 수 있다)

 

 

🍒 토큰 갱신 흐름 정리

 

나는 다음과 같이 정리했다.

 

 

 

백엔드는 최대한 일어날 수 있는 다양한 경우의 수를 생각해야 한다고 느꼈다. 많은 유저가 접속했을 때나 지속적인 요청을 보냈을 때 서버에 부담은 없는지, 토큰을 탈취당했다면 서버에서 어떠한 문제를 일으킬 수 있는지 등에 대한 최악의 상황도 고려해서 그런 부분들을 끝없이 고민해봐야 한다고 생각했다.

또한 내가 선택한 방법도 완전히 안전한 방법은 아니기 때문에 어떻게 하면 좀 더 사용자 인증을 안전하게 처리할 수 있을지에 대한 고민도 끝없이 해야하는 것 같다.

 

 

Teeny Box 프로젝트 시 정리했던 내용입니다. (2023.10.23 ~)

 

공유하기

facebook twitter kakaoTalk kakaostory naver band