지금까지의 프로젝트에서 JWT를 활용하여 사용자를 인증해 사용해왔지만, Access Token과 Refresh Token에 대해 제대로 된 이해와 고민 없이 단순히 구현에만 치중한 것 같아 관련 내용을 정리해보려 한다.
JWT를 활용한 토큰으로 사용자의 정보와 토큰의 암호화 키, 해시 알고리즘, 토큰의 만료시간 등 인증과 유효성 검증을 위한 정보가 담겨 있다.
이 토큰을 클라이언트가 헤더에 담아 요청하면 서버는 유효성을 검증하고, 클라이언트의 권한을 판별해 요청을 반환한다.
지금까지는 개발자와 이용자의 편의성을 고려해 유효시간을 길게 설정해 이용해왔지만 이는 토큰이 탈취 당할 경우를 무시한, 보안을 고려하지 않은 개발이다.
이를 보완하기 위해 Access Token과 Refresh Token을 활용하여 보안과 편의성을 최대한 지킬 수 있는 방안을 찾아보았다.
Access Token을 클라이언트가 Header에 담아 전송할 때, 공격자에게 탈취 당하더라도 짧은 유효시간을 설정해둔다면 비교적 피해를 줄일 수 있다.
하지만 Access Token의 유효시간을 줄이면 사용자는 잦은 로그인으로 불편함을 겪는다.
이런 불편함을 해소하기 위해 Refresh Token을 활용한다.
클라이언트가 서버에 어떤 요청을 하기 전, 기존에 가지고 있던 Access Token이 만료되었다면, Refresh Token을 통해 Access Token의 갱신을 요청하고 새로운 Access Token을 이용해 요청을 보낸다. 그렇기 때문에 Refresh Token은 Access Token보다 긴 유효시간을 가져야 한다.
기존의 Access Token이 만료되지 않았는데 Refresh Token을 이용해 갱신을 요청한다면, 이는 비정상적인 접근이라고 판단하고 기존의 Access Token과 Refresh Token을 모두 폐기해버린다.
여기서 폐기란 폐기하려는 토큰을 데이터베이스에 보관하여 Ban을 하는 것이다. 빠른 조회를 위해 Redis와 같은 비관계형 데이터베이스에 보관하며, 저장에 필요한 용량을 줄이기 위해 Refresh Token의 만료시기까지만 보관하여 불필요한 토큰을 저장하지 않도록 한다.
주로 클라이언트는 Access Token을 Local Storage나 Session Storage에 보관한다.
두 토큰이 모두 탈취 당하는 경우를 방지하기 위해, Refresh Token은 HTTP-ONLY 속성이 부여된 Cookie에 저장하는 것을 권장한다고 한다.
일부의 경우, State에 보관하거나 변수에 담아 보관한다고도 했으니 정답은 없는 것 같다.
최종적인 동작 방식을 정리하면 다음과 같다.
여러 자료를 찾아본 후, 나는 이 동작 방식이 가장 합리적인 방식이라고 생각하지만, 개발자나 어플리케이션에 따라 토큰의 이용방식이 다를 수 있고 정답은 없는 것 같다.