
권한이 필요한 요청은 서비스에서 많이 발생한다.(회원 CURD, 게시글/댓글 CRUD, 주문 서비스 등)
따라서 JWT는 매시간 수많은 요청을 위해 클라이언트의 JS 코드로 HTTP 통신을 통해 서버로 전달된다.
해커는 클라이언트 측에서 XSS를 이용하거나 HTTP 통신을 가로채서 토큰을 훔칠 수 있기 때문에 여러 기술을 도입하여 탈취를 방지하고 탈취되었을 경우 대비 로직이 존재한다.
위와 같은 문제가 발생하지 않도록 Access/Refresh 토큰 개념이 등장한다.
자주 사용되는 토큰의 생명주기는 짧게(약 10분), 이 토큰이 만료되었을 때 함께 받은 Refresh 토큰(24시간 이상)으로 토큰을 재발급한다.
(생명주기가 짧으면 만료시 매번 로그인을 진행하는 문제가 발생, 생명주기가 긴 Refresh도 함께 발급한다.)
단일 -> 다중 토큰으로 전환하며 자주 사용되는 Access 토큰이 탈취되더라도 생명주기가 짧아 피해 확률이 줄었다.
하지만 Refresh 토큰 또한 사용되는 빈도만 적을뿐 탈취될 수 있는 확률이 존재한다. 따라서 Refresh 토큰에 대한 보호 방법도 필요하다.
클라이언트에서 발급 받은 JWT를 저장하기 위해 로컬 스토리지와 쿠키에 대해 많은 고려를 한다. 각 스토리지에 따른 특징과 취약점은 아래와 같다.
로컬 스토리지: XSS 공격에 취약함: Access 토큰 저장
httpOnly(JavaScript 로 쿠키에 접근할 수 없으며, 브라우저의 통신이 일어날 때만 쿠키를 전달) 쿠키: CSRF(사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위(수정, 삭제, 등록 등)를 특정 웹사이트에 요청하게 하는 공격) 공격에 취약함: Refresh 토큰 저장
고려
JWT의 탈취는 보통 XSS 공격으로 로컬 스토리지에 저장된 JWT를 가져간다. 그럼 쿠키 방식으로 저장하면 안전하지 않을까라는 의문이 들지만, 쿠키 방식은 CSRF 공격에 취약하다. 그럼 각 상황에 알맞게 저장소를 선택해야 한다.
Access 토큰
Access 토큰은 주로 로컬 스토리지에 저장된다. 짧은 생명주기로 탈취에서 사용까지 기간이 매우 짧고, 에디터 및 업로더에서 XSS를 방어하는 로직을 작성하여 최대한 보호 할 수 있지만 CSRF 공격의 경우 클릭 한 번으로 단시간에 요청이 진행되기 때문이다.
권한이 필요한 모든 경로에서 사용되기 때문에 CSRF 공격의 위험보다는 XSS 공격을 받는게 더 나은 선택일 수 있다.
Refresh 토큰
Refresh 토큰은 주로 쿠키에 저장된다. 쿠키는 XSS 공격을 받을 수 있지만 httpOnly를 설정하면 완벽히 방어할 수 있다. 그럼 가장 중요한 CSRF 공격에 대해 위험하지 않을까라는 의구심이 생긴다.
하지만 Refresh 토큰의 사용처는 단 하나인 토큰 재발급 경로이다. CSRF는 Access 토큰이 접근하는 회원 정보 수정, 게시글 CRUD에 취약하지만 토큰 재발급 경로에서는 크게 피해를 입힐 만한 로직이 없기 때문.
위와 같이 저장소의 특징에 알맞은 JWT 보호 방법을 수행해도 탈취 당할 수 있는게 웹 세상이다. 따라서 생명주기가 긴 Refresh 토큰에 대한 추가적인 방어 조치가 있다.
Access 토큰이 만료되어 Refresh 토큰을 가지고 서버 특정 엔드포인트에 재발급을 진행하면 Refresh 토큰 또한 재발급하여 프론트측으로 응답하는 방식이 Refresh Rotate이다.
문제
로그아웃을 구현하면 프론트측에 존재하는 Access/Refresh 토큰을 제거한다. 그럼 프론트측에서 요청을 보낼 JWT가 없기 때문에 로그아웃이 되었다고 생각하지만 이미 해커가 JWT를 복제 했다면 요청이 수행된다.
위와 같은 문제가 존재하는 이유는 단순하게 JWT를 발급해준 순간 서버측의 주도권이 없기 때문이다.(세션 방식은 상태를 STATE하게 관리하기 때문에 주도권이 서버측에 있음)
로그아웃 케이스뿐만 아니라 JWT가 탈취되었을 경우 서버 측 주도권이 없기 때문에 피해를 막을 방법은 생명주기가 끝이나길 기다리는 방법이다.
방어 방법
생명주기가 긴 Refresh 토큰은 발급과 함께 서버측 저장소에도 저장하여 요청이 올때마다 저장소에 존재하는지 확인하는 방법으로 서버측에서 주도권을 가질 수 있다.
만약 로그아웃을 진행하거나 탈취에 의해 피해가 진행되는 경우 서버측 저장소에서 해당 JWT를 삭제하여 피해를 방어할 수 있다. (Refresh 토큰 블랙리스팅)
참고자료: 개발자 유미 스프링 JWT 심화