JWT 단독사용으로 발생할 수 있는 보안 문제 해결하기(Refresh Token)

junhyeong·2023년 5월 1일
0

JWT를 단독으로 쓰면 안되는 이유

이전에 프로젝트를 만들면서 JWT를 이용한 토큰 기반 인증을 구현했었다.

이 토큰은 세션과는 달리 다양한 플랫폼에서 토큰만으로 인증할 수 있다는 장점이 있어 유용하다.

그런데 내가 사용한 방식에는 문제가 있었다. 토큰 유효기간이 무제한이라 한번 탈취당하면 피해를 걷잡을 수 없는 것이다.

그 당시에 나는 JWT를 이용하면서 깊게 공부하지 않았고, 당연히 제대로 쓰지 않으면 이러한 문제점이 있다는 것도 알지 못했다.

그리고 그 당시의 기술부채가 쌓여서 지금 돌아왔다.

지금이라도 해결방법을 알아보자

유효시간 설정과 Refresh Token?

문제를 해결하기 위해 나는 JWT의 유효시간을 설정하는 방법을 찾아보고 공부하면서 Refresh Token 이라는 키워드를 알게 되었다.

만약 우리가 토큰의 유효기간을 하루로 설정했다고 생각해보자. 그러면 길지 않은 시간이지만 토큰이 탈취 당했을 때, 하루라는 시간동안 서버는 탈취당한 사람과 탈취한 공격자를 구분할 수 없게 된다.

이럴 때, 사용할 수 있는 것이 바로, Refresh Token이다.

Access Token, Refresh Token

Refresh Token은 사용자 인증이 아닌 새로운 Access Token을 생성하는 용도로만 사용된다.

방법은 다음과 같다.

  • Access Token: 유효 기간을 짧게 설정한다.
  • Refresh Token: 유효 기간은 길게 설정한다.
  • 사용자는 Access Token과 Refresh Token을 둘 다 서버에 전송하여 전자로 인증하고 만료됐을 시, 새로운 Access Token을 발급받는다.

이렇게 되면 공격자는 Access Token을 탈취하더라도 짧은 유효 기간이 지나면 사용할 수 없다는 장점이 있다.

물론 정상적인 클라이언트는 유효기간이 지나더라도 Refresh Token을 사용하여 새로운 Acccess Token을 생성하여 사용할 수 있으니 문제가 없다.

즉, 짧은 시간동안 사용할 수 있도록 하고 주기적으로 재발급을 하도록 하는 것이다. 이렇게 하면 앞에서 말했듯 토큰이 유출되어도 피해를 최소화할 수 있다.

하지만 단점도 있다.

정상적인 클라이언트도 짧은 주기마다 다시 로그인해서 Access Token을 발급받아야 하는 것이다. 그래서 유효 기간이 긴 Refresh Token을 사용하는데 정상적인 사용자는 Access Token이 만료됐다면 서버측에 Refresh Token을 전송하여 다시 로그인할 필요 없이 Access Token을 발급받을 수 있다. 당연히 이 Refresh Token이 없는 공격자는 다시 토큰을 발급받을 수 없기 때문에 보안 측면에서 좀 더 안전하다고 할 수 있다.

Refresh Token이 탈취된다면?

Refresh Token이 탈취되는 경우가 있을 수 있다. 그렇다면 어떻게 해야할까?

  • 데이터베이스에 각 사용자에 1 대 1로 맵핑되는 Access Token과, Refresh Token 쌍을 저장한다.
  • 정상적인 사용자는 기존의 Access Token으로 접근하며 서버측에서는 데이터베이스에 저장된 Access Token과 비교하여 검증한다.
  • 공격자는 탈취한 Refresh Token으로 새로 Access Token을 생성한다. 그리고 서버측에 전송하면 서버는 데이터베이스에 저장된 Access Token과 공격자에게 받은 Access Token이 다른 것을 확인한다.
  • 만약 데이터베이스에 저장된 토큰이 아직 만료되지 않은 경우, 즉 굳이 Access Token을 새로 생성할 이유가 없는 경우 서버는 Refresh Token이 탈취당했다고 가정하고 두 토큰을 모두 만료시킨다.
  • 이 경우 정상적인 사용자는 자신의 토큰도 만료됐으니 다시 로그인해야 한다. 하지만 공격자의 토큰 역시 만료됐기 때문에 공격자는 정상적인 사용자의 리소스에 접근할 수 없다.

중요한 것은 발급된 토큰 자체는 그냥 그 JWT 문자열 자체로 존재하는 것이기 때문에 클라이언트나 서버측에서 전역적으로 만료시킬 수 있는 개체가 아니다. 그렇기 때문에 토큰의 유효 기간이 지나기 전까지는 토큰을 데이터베이스에 저장할 필요가 있다.

토큰의 저장 장소

서버에서는 데이터베이스에 토큰을 저장할 수 있다. 그렇다면 클라이언트에서는 어디에 저장할 수 있을까

쿠키, 로컬 스토리지 등 다양한 곳이 있지만 스택오버플로우에서는 http-only 속성이 부여된 쿠키에 저장하는 것을 권장하고 있다.

해당 속성이 부여된 쿠키는 자바스크립트 환경에서 접근할 수 없기 때문이다. 그래서 XSS나 CSRF가 발생하더라도 토큰이 누출되지 않는다. 일반 쿠키나 브라우저의 로컬 스토리지는 자바스크립트로 자유롭게 접근할 수 있기 때문에 보안 측면에서는 권장되지 않는다.

마무리

어떤 기술을 사용하든 제대로 알고 사용하지 않으면 부작용이 생길 수 있다. 기술을 사용하기 전 확실하게 공부해두고(특히 주의할점) 기술을 적용하는 습관을 들이자

profile
매일매일이 성장하는 하루가 될 수 있도록!

0개의 댓글