들어가기
저번 로그인 방식 글에 이어서 JWT를 알아보자.
JWT 기반 로그인은 안전한가?
우선 토큰 기반의 로그인 방식은 서버가 생성은 하지만 관리는 클라이언트 측에서 하게된다.
클라이언트측은 토큰의 탈취 가능성을 염두해 예민한 정보는 토큰에 담지 않는다.
JWT를 구성하는 Header, Payload, Signiture 3가지 파트 중 Header와 Payload는 base 64로 인코딩 되지만
공격자가 디코딩이 가능하기때문에 유저의 식별정보만 담아야한다.
서버는 사용자 요청이 오면 AccessToken을 생성해 반환한다.
이후 인가처리는 AccessToken 유효성 검사를 통해 위변조를 감지할 수 있다.
AccessToken 탈취
AccessToken(이하 AT)을 탈취당할 경우를 대비해 토큰 만료기간을 매우 짧게 설정한다.(보통 1-2시간)
AT를 탈취당하더라도 짧은 시간안에 공격자의 접근을 막을 수 있다.
하지만 토큰 만료기간이 짧으면 그만큼 사용자는 토큰 만료마다 다시 로그인해야한다.
그렇기 때문에 서버는 AT와 함께 AT보다 만료기간이 긴 RefreshToken(이하 RT)를 같이 발급한다.
RT는 RDB에 저장한다.(RT 조회는 모든 인가과정에 참여하지 않기때문에 세션보단 부하가 덜하다.)
RT는 주로 2주정도로 긴 시간 살아남는다.
RefreshToken이 탈취당한다면?
RT가 탈취당한다면 또 골치가 아파진다.
RT가 2주간 지속된다하면 공격자는 긴 시간 사용자에게 피해를 입힐 수 있다.
Refresh Token Rotation(이하 RTR) 방식을 사용한다면 괜찮을까?
RTR는 AT만료 시 AT와 RT를 다시 발급하고 RT를 DB에 다시 업데이트한다.
RT를 공격자가 탈취하더라도 사용자가 공격자보다 먼저 AT와 RT를 재발급 받는다면 공격자가 훔친 RT는 더 이상 유효하지 않게된다.
공격자가 사용자보다 먼저 RefreshToken을 발급받는다면?
이렇게된다면 사용자의 RT가 더 이상 유효하지 않게 돼버린다.
마치 공격자가 정상적인 사용자이고 사용자가 공격자같은 엉뚱한 상황이된다.
그 과정을 좀 더 자세히 살펴보자
- 사용자 최초 요청이 서버에 도착해 AT-1, RT-1을 발급하고 DB에 RT-1을 저장한다.
- 공격자가 RT-1을 탈취하고 사용자보다 먼저 RT-1을 가지고 AT 재발급 요청을 보낸다.
- 서버는 공격자가 탈취한 RT-1을 DB의 RT-1과 비교하고 정상적인 접근으로 처리한다.
- 서버는 새로운 AT-2, RT-2를 공격자에게 발급하고 AT-1,RT-1은 더 이상 유효하지 않은 토큰이 된다.
- 사용자가 뒤늦게 RT-1을 이용해 서버에 재발급 요청을 하더라도 유효하지 않은 토큰이기 때문에 튕겨낸다.
요청받은 RefreshToken과 DB에 저장된 RefreshToken 다르면 무조건 튕겨내자
난 위에서 5번에 꽂혔다.
사실 정상적인 사용자 흐름은 요청한 RT와 저장된 RT가 다를 수 없다.
공격자가 RT를 탈취하고 사용자보다 먼저 갱신 요청 후 사용자가 만료된 RT 요청 -> 튕겨낸다(재로그인)
공격자가 RT를 탈취하고 사용자보다 늦게 갱신 요청 -> 튕겨낸다(재로그인)
간단히 말해 현재 DB에 존재하는 RT가 아니라면 무조건 재로그인을 하도록 만드는 방식이다.
이것도 완벽하진 않다.
사용자가 RT를 탈취당하고 오랫동안 그 사실을 인지하지 못한다면
공격자는 계속해서 AT를 발급받고 비정상적인 활동이 이루어질 수 있다.
위 방법은 활동하는 사용자들에게만 유용한 방법이긴하다.
물론 AT을 RT를 발급 후 같이 저장하고 재발급 요청이 AT 만료일 이전에 온다면 튕겨내는 방식이 있다.
그런데 이렇게 되면 토큰을 이용해 서버를 STATELESS하게 부담을 줄이려는 목적은 무색해진다.
심지어 AT,RT를 같이 탈취당하면 답도 없다.
완벽한 방패는 없다.
정리
우선 AT와 RT를 같이 사용하고 RTR방식으로 RT를 지속적으로 재발급할 생각이다.
서버에서 할 수있는 일은 사용자의 IP나 디바이스 정보를 통해서 다른 환경에서 로그인 됐다는 것을 알리는 방법이 최후의 방어막이 되지않을까 생각한다.
'Spring' 카테고리의 다른 글
[Spring Security JWT 로그인 (3)] DelegatingFilterProxy Deep Dive (0) | 2024.03.15 |
---|---|
[Spring Security JWT 로그인 (1)] 어떤 로그인 방식이 좋을까 (1) (0) | 2024.03.11 |
Redis Session Clustering [세션 고정] (0) | 2023.12.17 |
S3를 이용한 사용자의 고아 이미지 처리 (1) | 2023.12.02 |
코드의 중복과 Null 처리 (0) | 2023.09.25 |