JWT가 다른 토큰하고 가장 다른 부분은 토큰 자체가 데이터를 가지고 있다는 점이다.
일반적인 토큰의 흐름을 생각한다면 API 요청 시에 들어온 토큰을 보고 이 토큰이 유효한지 확인하게 된다. 보통은 데이터베이스에 토큰을 저장해 놓고 만료시간이나 토큰의 사용자 등을 저장해 놓고 유효한 토큰인지 등을 검사하고 유효한 경우 해당 사용자라고 인식하고 이 사용자의 권한으로 사용할 수 있는 정보를 조회하게 된다. 요청마다 데이터베이스를 조회하는 것은 비용이 꽤 크므로 캐시서버 등을 두어 성능을 높이기도 한다.
JWT의 경우는 토큰을 받아서 서명으로 유효한 토큰인지 검증을 한 뒤에 유효하다고 판단하면 클레임셋을 디코딩해서 토큰에 담긴 데이터를 열어본다.
JWT 단점
- 앞에서 보았듯이 클레임셋은 암호화하지 않는다. 그래서 서명 없이도 누구나 열어볼 수 있기 때문에 여기에는 보안이 중요한 데이터는 넣으면 안 된다. base64로 인코딩해서 사용하다 보면 이 부분을 간과하기 쉬운데 필요한 최소한의 정보만 클레임셋에 담아야 한다.
- 인코딩 특성상 클레임셋의 내용이 많아지면 토큰의 길이도 같이 길어진다. 그래서 편하다고 너무 많은 정보를 담으면 안 된다. 앞에서 정의된 클레임네임이 전부 약자를 사용하는 이유도 JWT를 최대한 짧게 만들기 위함이다.
- 토큰을 강제로 만료시킬 방법이 없다. 서버가 토큰의 상태를 가지고 있지 않고 토큰 발급 시 해당 토큰이 유효한 조건이 결정되므로 클라이언트가 로그아웃하더라도 해당 토큰을 클라이언트가 제거하는 것뿐이지 토큰 자체가 만료되는 것은 아니다. 만약 이때 누군가 토큰을 탈취한다면 해당 토큰을 만료시간까지는 유효하게 된다. 많은 고민을 해보았지만 무상태를 유지하면서 이 부분을 같이 해결할 방법은 존재하지 않으므로 서비스에서 이 부분이 문제가 된다면 선택을 해야 한다고 본다.
무엇보다 JWT의 가장 큰 장점은 앞서 말했듯이 DB를 조회하지 않아도 된다는 것이 제일 큰 장점이다. CPU 부하가 생길수는 있지만 현재 API 인증 서버에서 CPU가 문제 되는 경우는 거의 없다고 생각함. 이런 문제보다 DB 조회와 캐시서버 관리가 훨씬 많은 문제를 일으킨다. JWT를 쓰면 토큰과 마찬가지로 위변조를 방지할 수 있으면서 DB를 조회하지 않고, JWT를 검사할 수 있으므로 서버를 stateless로 유지할 수 있다. 똑같은 보안을 유지하면서 서버가 stateless가 된다는 부분은 아주 큰 장점이다.
과거의 Stateful 서버처럼 클라이언트와의 통신상태를 계속 추적하고 이를 자신의 서비스 제공에 이용하는 서버(과거 스프링 프레임워크 JSP 기반의 백엔드와 프론트엔드가 구분이 없는 상황) 클라이언트가서버에 접속하면 특정 서버와 계속해서 연결 상태에서 있어야 된다. 이말은 즉 클라이언트는 전적으로 서버에 의존적이게 된다.
반면에 최신 프론트엔드(ReactJS, VueJS 등) 및 백엔드(API 서버) 같은 경우, 완전 분리 독립적인 환경에서 개발을 많이 하는데 이런 구조인 경우에 백엔드를 Stateless Server라고 볼 수 있다. 무상태(Stateless)인 경우 클라이언트와 서버간의 의존성이 없기 때문에 클라이언트에서 요청을 보낼 때 어떤 서버든 손쉽게 서비스할 수 있는 구조이기 때문에 웹 서버를 확장한다고 했을 때 복잡도가 줄어든다.
토큰을 탈취당했을 때 대처방안
공격자가 가로챈 요청의 사용자인척 중요 정보를 볼 수 있는 문제가 발생한다.
이는 간단히 보면 아이디/패스워드를 뺏긴것과 동일하기 때문에 원천적으로 막지는 못한다.
왜냐하면 공격자가 아이디/패스워드를 넣으면 로그인할 수 있다는 문제를 막을 수 있는 방법은 없기 때문이다.
이는 보통 토큰을 엑세스토큰과 리프레시 토큰으로 나누어서 관리하는 방식으로 처리한다.(JWT 토큰이냐 아니냐는 상관없음)
엑세스 토큰은 만료시간을 10분 주고 리프레시 토큰은 더 길게 준 뒤에 엑세스토큰으로 API를 사용하다가 만료되면 리프레시토큰을 이용해서 다시 엑세스토큰을 발급받는 방식이며 일반적으로 API 요청에는 리프레시토큰을 같이 보내지 않습니다.
이 경우 네트워크에서 토큰을 뺏기더라도 만료시간 만큼만 사용할 수 있고 이 후에는 재발급받지 못하므로 더이상 사용하지 못하게 된다.