요약
AuthTokenService.rotate는 이미 소비된 jti로 들어온 요청에 401을 돌려주고 WARN만 남긴다. 그 회원의 다른 Refresh 토큰은 건드리지 않는다. 토큰이 유출되면 공격자가 먼저 회전해 새 토큰을 얻고, 정상 사용자만 401로 끊긴다 — 공격자 세션은 최대 7일 살아남는다.
Jira (필수)
상위/관련 GitHub Issue (선택)
재현 절차
- 한 회원이 두 기기에서 로그인해 서로 다른 Refresh 토큰
A·B를 갖는다
A로 POST /api/core/v1/auth/refresh → 성공(204), A는 소비되고 A'가 발급된다
A를 다시 사용해 재발급을 시도한다 → 401 (재사용 감지)
- 그 상태에서
B로 재발급을 시도한다
기대 / 실제
- 기대: 3단계에서 재사용이 감지된 순간 그 회원의 Refresh 토큰이 전부 폐기되어, 4단계의
B도 401이 된다
- 실제:
B는 그대로 유효해 재발급이 성공한다. A'(공격자가 받았을 토큰)도 살아 있다
원인 / 해결 방향
RefreshTokenStore가 auth:refresh:<memberId>:<jti> 키를 jti마다 하나씩 두고 삭제로 소비한다. 세션 독립성을 위한 의도된 구조(BD-21)지만, 회원 단위로 모아 볼 수단이 없다.
- 회원별
jti 인덱스를 추가해 회원 단위 일괄 폐기를 만든다
SCAN은 쓰지 않는다. auth:refresh:<memberId>:* SCAN은 키 공간에 비례해 운영 Redis에서 권장되지 않는다(BD-32에서 이미 기각한 안)
rotate의 재사용 감지 분기에서 그 폐기를 호출한다
- 이 인덱스는 회원 탈퇴(S15P11A705-65) 가 필요로 하는 것과 같은 연산이다. BD-32에 "두 작업을 같이 하는 것이 자연스럽다"고 적혀 있다
근거: RFC 9700 §4.14.2 — 재사용 감지 시 해당 사용자의 refresh 토큰을 전부 폐기하라
완료 조건
참고
BD-32가 감수 사항으로 기록해 둔 내용 — "Refresh가 유출되면 공격자 세션이 최대 7일 살아남는다. 이것은 BD-21의 무효화 지연(로그아웃 후 Access 30분)과 다른 구멍이다. 그쪽은 시간이 지나면 닫히고, 이쪽은 공격자가 계속 회전하는 한 닫히지 않는다."
요약
AuthTokenService.rotate는 이미 소비된jti로 들어온 요청에 401을 돌려주고 WARN만 남긴다. 그 회원의 다른 Refresh 토큰은 건드리지 않는다. 토큰이 유출되면 공격자가 먼저 회전해 새 토큰을 얻고, 정상 사용자만 401로 끊긴다 — 공격자 세션은 최대 7일 살아남는다.Jira (필수)
상위/관련 GitHub Issue (선택)
docs/backend/decisions/BD-32-refresh-reuse-no-family-revocation.md— 이 이슈가 그 결정의 재검토 트리거다재현 절차
A·B를 갖는다A로POST /api/core/v1/auth/refresh→ 성공(204),A는 소비되고A'가 발급된다A를 다시 사용해 재발급을 시도한다 → 401 (재사용 감지)B로 재발급을 시도한다기대 / 실제
B도 401이 된다B는 그대로 유효해 재발급이 성공한다.A'(공격자가 받았을 토큰)도 살아 있다원인 / 해결 방향
RefreshTokenStore가auth:refresh:<memberId>:<jti>키를jti마다 하나씩 두고 삭제로 소비한다. 세션 독립성을 위한 의도된 구조(BD-21)지만, 회원 단위로 모아 볼 수단이 없다.jti인덱스를 추가해 회원 단위 일괄 폐기를 만든다SCAN은 쓰지 않는다.auth:refresh:<memberId>:*SCAN은 키 공간에 비례해 운영 Redis에서 권장되지 않는다(BD-32에서 이미 기각한 안)rotate의 재사용 감지 분기에서 그 폐기를 호출한다근거: RFC 9700 §4.14.2 — 재사용 감지 시 해당 사용자의 refresh 토큰을 전부 폐기하라
완료 조건
SCAN없이 동작BD-32의 상태 갱신 (문서 삭제 없이 상태만)./gradlew clean check --no-daemon통과참고
BD-32가 감수 사항으로 기록해 둔 내용 — "Refresh가 유출되면 공격자 세션이 최대 7일 살아남는다. 이것은 BD-21의 무효화 지연(로그아웃 후 Access 30분)과 다른 구멍이다. 그쪽은 시간이 지나면 닫히고, 이쪽은 공격자가 계속 회전하는 한 닫히지 않는다."