2 min read
OAuth Token Rotation 하드닝
Refresh Token 재사용 탐지와 세션 무효화 정책을 통한 계정 탈취 대응 방식

도입
OAuth 토큰 회전은 권장사항이 아니라 장기 운영 서비스의 필수 보안 장치다. refresh token을 고정 키처럼 사용하면 탈취 시 피해 범위가 커지고, 이상 징후 탐지도 어려워진다. 이 글은 rotation, reuse detection, 세션 무효화 정책을 포함한 하드닝 전략을 정리한다.

문제 정의
토큰 회전이 형식적으로만 구현된 시스템은 보안 사고 시 대응이 매우 어렵다.
- refresh token 재사용 탐지가 없어 탈취된 토큰이 장기간 사용된다.
- 토큰 패밀리 상태를 저장하지 않아 연쇄 무효화가 불가능하다.
- 로그아웃 시 access token만 폐기하고 refresh token은 그대로 유지된다.
보안 관점에서 핵심은 단일 토큰이 아니라 토큰 패밀리 관리다. 한 번의 재사용이 발견되면 연관 세션 전체를 차단해야 한다.
핵심 개념
| 관점 | 설계 기준 | 검증 포인트 |
|---|---|---|
| 회전 | 요청마다 refresh token 재발급 | 동시 요청 충돌률 |
| 재사용 탐지 | jti/nonce 기반 탐지 | 의심 세션 발견 시간 |
| 무효화 | 패밀리 단위 revoke | 강제 로그아웃 반영 시간 |
| 감사 | 보안 이벤트 로깅 | 사고 조사 소요 시간 |
토큰 보안은 인증 서버 단일 기능으로 끝나지 않는다. API 게이트웨이와 세션 저장소, 알림 체계를 함께 설계해야 실효성이 있다.
코드 예시 1: 토큰 패밀리 모델
export type RefreshTokenFamily = {
familyId: string;
userId: string;
currentJti: string;
revokedAt?: string;
lastSeenIp?: string;
};
export async function rotateRefreshToken(family: RefreshTokenFamily, nextJti: string) {
if (family.revokedAt) throw new Error("family revoked");
await tokenStore.updateFamily(family.familyId, {
currentJti: nextJti,
});
return signRefreshToken({ familyId: family.familyId, jti: nextJti });
}
코드 예시 2: 재사용 탐지 로직
export async function verifyRefreshToken(payload: { familyId: string; jti: string }) {
const family = await tokenStore.findFamily(payload.familyId);
if (!family) throw new Error("invalid family");
if (family.currentJti !== payload.jti) {
await tokenStore.revokeFamily(payload.familyId, "reuse detected");
throw new Error("refresh token reuse detected");
}
return family;
}
아키텍처 흐름
Mermaid diagram rendering...
트레이드오프
- 회전 로직을 강화하면 보안이 향상되지만 토큰 저장소 부하가 증가한다.
- 재사용 감지는 강력하지만 동시성 처리 미흡 시 정상 사용자까지 로그아웃될 수 있다.
- 패밀리 무효화는 안전하지만 UX 측면에서 세션 유지성이 떨어질 수 있다.
정리
OAuth 하드닝의 핵심은 refresh token을 상태 있는 자산으로 관리하는 것이다. 회전과 재사용 탐지를 결합하고 패밀리 단위 무효화를 적용하면 계정 탈취 리스크를 실질적으로 줄일 수 있다.
이미지 출처
- Cover: source link
- License: CC BY-SA 4.0 / Author: Nhatem3
- Note: Wikimedia Commons 무료 라이선스 이미지를 다운로드 후 1600px 기준 JPG로 최적화했습니다.