엘리스 SW 엔지니어 트랙/Project

Refresh Token 구현 기록(2)

wookhyung 2022. 6. 14. 20:42
728x90

이전 글에서 리프레쉬 토큰을 알게 된 계기와 간단한 정리까지 해봤다.

 

그래서 구현을 하려는데.. 구현에 앞서 생각해봐야 될 점은 토큰은 어디에 저장돼야 할까?

 

기존 프로젝트에 구현된 JWT 토큰의 문제점은,

1. 만료 기간이 존재하지 않았다. (탈취 당했을 때 굉장히 위험하다.)
2. 세션 스토리지에 저장되어 있었다. (사용자가 브라우저 탭을 닫았을 때 토큰이 삭제된다.) 

 

여기서, 2번에 대해 더 추가적인 설명을 하자면 탭을 닫았을 때 토큰이 삭제되면 무슨 문제가 생길까?

 

사용자가 조금 전에 로그인을 했음에도 불구하고 탭을 닫았다가 다시 접속하면 재로그인을 해야 되는 문제가 생긴다. 사용자 입장에서는 조금 전까지도 접속을 해있었는데 다시 로그인을 해야되므로 불편함을 느낄 수 있는 부분이다.

 

그렇다면 남은 선택지는 1. 로컬 스토리지와 2. 쿠키 정도가 있는데 어디에 저장하는 것이 옳을까? 결론부터 말하자면, 이 질문에 대한 정답은 없다. 로컬 스토리지에 저장하는 곳도 있고 쿠키에 저장하는 곳도 있다.


일단 로컬스토리지 저장 방식에 대해 먼저 알아보자.

 

로컬 스토리지도 세션 스토리지와 같이 브라우저에 저장되는 방식이지만, 세션 스토리지와 달리 탭을 닫았을 때 사라지지 않고 사용자가 직접 지우지 않는 이상 브라우저에 남아있게 된다. 또한 브라우저 내에 저장되므로 Javascript 내 글로벌 변수로 읽기 / 쓰기 접근이 가능하다. 쉽게 클라이언트측에서 접근할 수 있다는 점이 장점이자 단점으로 작용할 수 있다.

=> 해커는 크로스 사이트 스크립팅(XSS)을 통해 그 안에 담긴 값을 불러오거나, 불러온 값을 이용해 API 요청을 위조할 수 있다.

 

그럼 쿠키 방식을 사용해야될까?

 

먼저, 쿠키도 자바스크립트 내 글로벌 변수로 접근이 가능하다. 하지만 httpOnly 라는 옵션을 사용하면 클라이언트 측에서 자바스크립트로 접근이 불가능하기 때문에 XSS를 방지할 수 있다. 그럼 쿠키를 사용하는 것이 좋은거 아닌가? 라고 생각할 수 있지만 헤더에 따로 담지 않아도 모든 요청에 담기는 쿠키의 특성 상 CSRF라는 문제점이 생긴다.

 

결국 어디에 저장을 하더라도 취약점은 존재하고 토큰 탈취를 완벽하게 방지할 수 없는 것 같다. 따라서 클라이언트 및 서버 측에서 추가적인 방어 조치는 보안을 위해서 필수불가결하다. 예를 들어, 리프레쉬 토큰을 검증하는 과정이 서버 측에서 처리가 되는데 해당 유저의 IP를 검증하는 방법이 있다. 네이버나 구글, 페이스북 등에서 평소와 다른 기기나 위치에서 접속하면 경고 알람이 왔던걸 본 적이 있다. 하지만 이런 방법을 사용해도 IP는 변조가 가능하다는 문제가 있고, 모든 유저의 IP와 Refresh token이 DB에 저장되어 검증 과정을 거칠 때마다 빈번하게 조회되기 때문에 stateful 해진다는 단점이 있다. JWT 토큰의 stateless 하다는 장점을 이용하려는 취지와 어긋난다. 굳이 토큰을 사용해야 되나? 라는 결론에 다다를수도 있다.


결론

토큰에 대해서는 정말 정해진 정답이 없는 것 같다. 구현을 하면서도 느낀거지만 구현하는 방식조차도 사람들마다 꽤 다르고 생각해야 될 부분이 정말 많다. 100% 완벽한 보안은 존재하지 않는다는 말을 체감하게 된다..