[공부정리] Refresh Token Rotation 도입 및 Token 관리 전략 소개

jeyong·2024년 3월 11일
0

공부 / 생각 정리  

목록 보기
46/120
post-custom-banner


지난번에 작성했던, [생각정리] JWT의 Stateless 고찰에서 언급한 바와 같이, JWT의 Stateless한 특성에 집착하지 않기로 했다. 이 결정을 바탕으로, 프로젝트에 Redis를 활용한 Refresh Token Rotation 방식을 도입하였다. 또한 프로젝트 토큰 관리 전략도 개선 하였다. 이번 글에서 해당 내용들을 소개하도록 하겠다.

1. Refresh Token Rotation

Refresh Token Rotation은 보안 강화를 위한 추가 조치로, 사용자가 인증을 갱신할 때마다 Refresh Token도 교체하는 방식이다. 이 방법은 Refresh Token의 수명을 최소화하고, 재사용을 방지하여 보안성을 높인다. 자세한 내용은 아래 게시글을 참고하는 것을 추천한다.

인증과 인가를 안전하게 처리하기 (Refresh Token Rotation)

구글과 같은 서비스도 계속해서 사이트를 방문하면 일주일 ~ 한달 동안 로그인이 끊어지지 않지만, 이틀 정도 방문하지 않으면 세션이 만료되며 로그아웃이 된다. Rotation을 통한 지속적인 로그인을 구현했기 때문이다.

2. 코드 구현

Refresh Token Rotation을 구현한 코드를 보며 설명하겠다. 적용한 프로젝트의 링크를 아래에 첨부하겠다.

O2O-Automatic-Store_Object-Detection_Demo

2-1. 로그인

@Transactional
   public SignInResponse signIn(SignInRequest req) {
       Member member = memberRepository.findWithRolesByEmail(req.getEmail()).orElseThrow(LoginFailureException::new);
       validatePassword(req, member);
       TokenHandler.PrivateClaims privateClaims = createPrivateClaims(member);
       String accessToken = userAccessTokenHandler.createToken(privateClaims);
       String refreshToken = userRefreshTokenHandler.createToken(privateClaims);
       redisHandler.setValues(String.valueOf(member.getId()), refreshToken);
       return new SignInResponse(accessToken, refreshToken);
   }

사용자가 로그인을 요청할 때, 사용자 ID를 사용하여 Refresh Token을 Redis에 저장한다.

  • 보안 강화: 서버 측에서 Refresh Token을 관리함으로써, 만약 토큰이 탈취되더라도 탈취된 토큰을 통한 액세스를 제한할 수 있다.
  • 토큰 관리: 서버에서 Refresh Token의 유효성을 주기적으로 확인하고, 필요한 경우 갱신하거나 무효화할 수 있다.

2-2. 토큰 재발급

@Transactional
   public UserRefreshTokenResponse refreshToken(String userRefreshToken) {
       TokenHandler.PrivateClaims userClaims = userRefreshTokenHandler.parse(userRefreshToken)
               .orElseThrow(RefreshTokenFailureException::new);
       String storedToken = Optional.ofNullable(redisHandler.getValues(userClaims.getId()))
               .orElseThrow(RefreshTokenFailureException::new);

       if (!storedToken.equals(userRefreshToken)) {
           redisHandler.deleteValues(String.valueOf(userClaims.getId()));
           throw new RefreshTokenFailureException();
       }

       String newUserAccessToken = userAccessTokenHandler.createToken(userClaims);
       String newUserRefreshToken = userRefreshTokenHandler.createToken(userClaims);

       redisHandler.setValues(String.valueOf(userClaims.getId()), newUserRefreshToken);

       return new UserRefreshTokenResponse(newUserAccessToken, newUserRefreshToken);
   }

Refresh Token 갱신 요청 시, 저장된 Refresh Token과 요청 받은 Token을 비교한다. 불일치하거나 없는 경우, 오류를 반환하고 저장된 Token을 삭제한다. 문제가 없는 경우에는 Access Token과 함께 Refresh Token도 갱신한다.

  • 재사용 방지: 이미 사용되었거나 무효화된 Refresh Token의 재사용을 방지하여, Replay Attack과 같은 보안 위협으로부터 보호한다.
  • 보안 위반 감지: 요청된 Refresh Token과 저장된 Token이 일치하지 않는 경우, 이는 보안 위반이 발생했을 가능성을 의미한다.
  • 보안성 향상: 각 인증 갱신마다 새로운 Refresh Token을 발급함으로써, 토큰 탈취 시 공격자가 오래된 토큰을 사용하는 것을 방지한다.
  • 토큰 수명 관리: Refresh Token의 수명을 짧게 유지함으로써, 잠재적 보안 위험을 최소화한다.

2-3. 회원 탈퇴

@Transactional
    public void delete(Long id) {
        Member member = memberRepository.findById(id).orElseThrow(MemberNotFoundException::new);
        Boolean exists = redisHandler.exists(String.valueOf(id));
        if (exists != null && exists) {
            redisHandler.deleteValues(String.valueOf(id));
        }
        memberRepository.delete(member);
    }

사용자가 계정을 삭제할 경우, 관련된 Refresh Token도 함께 삭제한다.

  • 데이터 일관성: 사용자 데이터와 관련된 모든 정보를 삭제함으로써 데이터의 일관성을 유지한다.
  • 보안 위험 제거: 탈퇴한 사용자의 토큰을 계속해서 저장하고 관리하는 것은 불필요한 보안 위험을 만든다.

3. 프로젝트 토큰 구조 및 관리

3-1. 토큰 구조

프로젝트의 토큰 구조는 아래와 같다.

  • 사용자의 Refresh Token: 사용자가 로그인을 할 때 발급받는 토큰으로, Access Token을 갱신할 때 사용된다. 이 토큰을 통해 사용자는 새로운 Access Token을 얻을 수 있다.
  • 사용자의 Access Token: 인증된 사용자가 시스템 내의 리소스에 접근할 수 있도록 하는 토큰이다. 유효 기간은 비교적 짧으며, 만료 시 Refresh Token을 통해 갱신해야 한다.
  • 상점의 Access Token: 사용자가 상점 관련 작업을 수행할 때 사용되는 토큰이다. 사용자의 Access Token을 통해 상점의 Access Token을 갱신할 수 있다.

3-2. 토큰 관리

  • Refresh Token의 안전한 관리: Refresh Token은 비교적 긴 유효 기간을 가지며, 시스템에서는 이를 안전하게 관리하여야 한다. Refresh Token의 재사용을 방지하기 위해, 사용자가 인증 정보를 갱신할 때마다 새로운 Refresh Token을 발급하고 이전 토큰을 무효화하는 Refresh Token Rotation 방식을 적용한다.
  • Access Token의 짧은 유효 기간: Access Token은 짧은 유효 기간을 짧게 설정하여, 만약 Access Token이 탈취되더라도, 짧은 유효 기간으로 인해 공격자가 오랜 시간 동안 유효한 토큰을 사용할 수 없도록 한다.
  • 토큰 저장의 최소화: 시스템은 Refresh Token만을 저장함으로써, 데이터베이스나 캐시 시스템에서 관리해야 할 토큰의 수를 최소화한다.

이러한 Refresh Token Rotation 전략과 토큰 관리 방식을 도입함으로써, 사용자의 로그인 상태를 지속적으로 유지하면서도, 보안 위험을 크게 줄일 수 있었다.

profile
노를 젓다 보면 언젠가는 물이 들어오겠지.
post-custom-banner

0개의 댓글