💚 어떤 기능인가요?
AuthController.refreshToken 이 jwtService.validateToken 을 쓴다.
이 검증은 서명·만료·issuer 만 보므로 Access Token 도 통과한다.
JwtService 는 이미 typ 클레임으로 종류를 구분하고 validateRefreshToken 을 제공한다.
refreshTokens() 메서드에는 "Access Token 을 Refresh 엔드포인트로 재사용하는 것을 차단합니다"
라는 주석까지 있는데, 컨트롤러가 그 메서드를 쓰지 않고 로직을 재구현하면서 약한 검증을 썼다.
뒤따르는 refreshTokenStore.isRegistered 가 2차 방어인데,
기본 설정이 jwt.refresh-token.store=none 이고 NoopRefreshTokenStore.isRegistered 는
무조건 true 를 반환한다. 즉 기본 프로파일에서 방어가 없다.
결과: 탈취한 1시간짜리 Access Token 을 30일짜리 Refresh Token 으로 교환할 수 있다.
✅ To Dos
💚 어떤 기능인가요?
AuthController.refreshToken이jwtService.validateToken을 쓴다.이 검증은 서명·만료·issuer 만 보므로 Access Token 도 통과한다.
JwtService는 이미typ클레임으로 종류를 구분하고validateRefreshToken을 제공한다.refreshTokens()메서드에는 "Access Token 을 Refresh 엔드포인트로 재사용하는 것을 차단합니다"라는 주석까지 있는데, 컨트롤러가 그 메서드를 쓰지 않고 로직을 재구현하면서 약한 검증을 썼다.
뒤따르는
refreshTokenStore.isRegistered가 2차 방어인데,기본 설정이
jwt.refresh-token.store=none이고NoopRefreshTokenStore.isRegistered는무조건
true를 반환한다. 즉 기본 프로파일에서 방어가 없다.결과: 탈취한 1시간짜리 Access Token 을 30일짜리 Refresh Token 으로 교환할 수 있다.
✅ To Dos
validateToken→validateRefreshToken으로 교정jwt.refresh-token.store기본값을redis로 (none 이면 로그아웃이 무작동이라 탈취 토큰을 회수할 수 없다)