Skip to content

[FIX] 백오피스 강제 탈퇴 시 대상 회원의 액세스 토큰이 무효화되지 않음 #462

Description

@2ghrms

🐛 버그 설명

현상

백오피스에서 회원을 강제 탈퇴시켜도, 해당 회원이 이미 발급받은 액세스 토큰으로 만료 시점까지 API를 계속 호출할 수 있습니다.

기대 동작

강제 탈퇴 처리된 회원은 즉시 인증이 차단되어야 합니다.

실제 동작

리프레시 토큰은 삭제되므로 재발급은 막히지만, 이미 손에 있는 액세스 토큰은 그대로 통과합니다. 액세스 토큰 만료 시간만큼의 공백이 발생합니다.

원인

BackofficeMemberServiceImpl.forceWithdrawMember()WithdrawalService.withdrawMember(Member)를 직접 호출합니다. 이 메서드가 수행하는 작업은 다음과 같습니다.

  • member.withdraw() — 소프트 삭제
  • deviceTokenRepository.deleteAllByMemberId() — FCM 디바이스 토큰 삭제
  • jwtUtil.removeAllRefreshTokens() — 리프레시 토큰 삭제

반면 사용자 본인이 호출하는 withdrawCurrentUser()는 여기에 더해 jwtUtil.blacklistAccess(rawAccessToken)를 수행합니다. 즉 액세스 토큰 무효화는 요청 헤더에 토큰이 실려 오는 본인 탈퇴 경로에만 존재합니다.

백오피스 경로에는 대상 회원의 액세스 토큰이 없으므로 현재 구조로는 블랙리스트 등록 자체가 불가능합니다. JwtUtil.blacklistAccess()jti 단위로만 동작하기 때문입니다.

public void blacklistAccess(String accessToken) {
    Claims claims = validateTokenOnlySignature(accessToken);
    String jti = claims.getId();
    long ttlSeconds = Math.max(1, (claims.getExpiration().getTime() - System.currentTimeMillis()) / 1000);
    redisTemplate.opsForValue().set("blacklist:" + jti, "1", ttlSeconds, TimeUnit.SECONDS);
}

참고

같은 한계가 본인 탈퇴에도 부분적으로 존재합니다. 여러 기기에서 로그인한 상태라면 탈퇴 요청에 사용한 토큰 하나만 블랙리스트에 등록되고, 다른 기기의 액세스 토큰은 만료까지 유효합니다. 이 내용은 #461 에서 회원 탈퇴 API Swagger 문구로 명시해두었습니다.

✅ 수정 목록

  • 회원 단위 액세스 토큰 무효화 수단 도입 여부 결정
  • forceWithdrawMember 경로에 적용
  • 본인 탈퇴(withdrawCurrentUser) 및 백오피스 승인 취소 등 다른 강제 차단 경로에도 동일하게 적용할지 검토
  • 회원 탈퇴 / 강제 탈퇴 API Swagger description 갱신
  • 테스트 추가

📝 참고 사항

방향 제안 — 회원 단위 무효화 컷오프

액세스 토큰에는 이미 iat(발급 시각) 클레임이 있고(JwtUtilsetIssuedAt), 블랙리스트 검사는 JwtAuthFilterassertNotBlacklisted() 한 곳에서만 이루어집니다. 따라서 다음 방식이 비교적 적은 변경으로 가능합니다.

  1. 탈퇴/차단 시 Redis에 revoked-before:{memberId} = now 를 액세스 토큰 만료 시간만큼 TTL을 걸어 저장
  2. JwtAuthFilter에서 토큰의 iat가 해당 값보다 이전이면 거부

이렇게 하면 대상 회원의 토큰을 들고 있지 않아도 발급된 모든 액세스 토큰을 한 번에 무효화할 수 있습니다. 기존 jti 블랙리스트(로그아웃용)는 그대로 두고 병행하면 됩니다.

대안으로 Member에 토큰 버전 컬럼을 두고 JWT 클레임에 실어 비교하는 방식도 있으나, 매 요청마다 DB 조회가 필요하거나 캐싱이 추가로 필요합니다.

보안 영향도

액세스 토큰 만료 시간 범위 내로 노출 창이 제한되므로 치명적이지는 않으나, 제재 목적의 강제 탈퇴가 즉시 효력을 갖지 못한다는 점에서 수정이 필요합니다.

관련

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

🐛 bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions