jwt 관한 논의를 보면 경력이나 실력에 상관없이 중구난방인 케이스가 많이 보여서 간단히 정리해본다.
jwt 토큰 구현은 여러가지 방법이 존재한다.
보통 엑세스와 리프레쉬 토큰으로 토큰을 구분하는 방식과 그냥 토큰 하나로 관리하는 방식이 존재한다.
토큰 하나로 관리 하는 방식의 경우 토큰 자체가 가진 권한이 매우 크기에 토큰 자체를 유저가 발급하고 그 권한을 설정할 수 있게 해 줄 필요가 있다.
또는 매우 단기간에만 사용 가능하도록 하는게 보안상 유리하다. 보통 사용자가 관리에 대해서 많은 권한을 가진 구현은 대표적으로 깃허브 토큰을 예로 들 수 있다. 깃허브 토큰의 경우 사용자가 토큰 발급과 권한 및 삭제까지 모두 관리한다.
토큰 권한과 유효 기간을 단축하여서 보안상 이점을 보는 방식으로는 대표적으로 소셜 로그인 방식을 볼 수 있다. 대부분 리프레쉬 토큰을 같이 제공하지만 일부 로그인 방식의 경우 리프레쉬 토큰을 제공하지 않으며 짧은 시간 동안 액세스가 가능한 토큰만을 제공하고 또 제한된 정보와 인증 정보만을 제공한다.
주로 이렇게 나뉘는 이유는 보통 토큰 방식의 경우 매우 큰 권한을 가지는데 비해 양자간 연결로 유지되는 세션과 달리 토큰 자체가 탈취되기 용이하기 때문이다.
그렇다면 어째서 토큰을 사용할까?
인터넷의 발달은 인터넷 트래픽 규모의 증가와 그에 대응하는 인프라의 발전을 불러왔다
그로 인해서 기존의 단순한 모놀리스 식 아키텍쳐에서 각 상황과 서비스 특징에 특화된 대응이 가능한 MSA 아키텍처가 각광받기 시작했으며 기존 모놀리스식의 서비스보다는 여러 부분으로 나누어진 MSA형식의 서비스가 대세가 되었다. 그러다 보니 세션과 다른 여러 부분으로 나뉜 낮은 결합도를 가진 기능들을 효과적으로 응집시켜서 사용자에게 서비스하기 위한 인증 방법이 필요했는데 이 중 하나가 JWT토큰이다.
토큰의 등장으로 인해서 인증 서버 하나로 여러가지 작게 분리된 기능들을 손쉽게 응집시켜서 유저에게 서비스를 제공할 수 있게 되었다.
그러나, 그러다 보니 권한 그 자체인 토큰이 가진 특징인 낮은 결합도로 인해서 보안적으로 탈취하기가 용이해졌고 이를 보완하기 위해서 여러 가지 방안들이 지속적으로 연구되고 적용되고 있다.
액세스-리프레쉬 토큰 구조도 그 결과물의 하나이며 지금은 여러 가지 분야에서 대표적으로 많이 쓰이는 토큰 구조중 하나이다. 이는 본질적으로는 토큰이라는 막대한 권한을 2가지로 분리하여서 권한의 무제한적 사용에 브레이크를 거는 방식이라고 생각하면 된다.
그렇기에 엑세스 토큰, 리프레쉬 토큰 jwt 구조를 구현한다면 두 토큰 간의 결합을 통해서 서로를 견제하는 방향성을 가지게 되는 게 정상적이다.
만약 해당 구조가 필요치 않은 상황이라면 굳이 토큰을 리프레쉬와 엑세스로 나누지 말자.
액세스 토큰과 리프레쉬 토큰이란?
이해를 돕기 위해서 간단히 비유를 들자면 액세스 토큰과 리프레쉬 토큰은 왕과 옥새에 해당한다.
둘은 사실상 불가분의 관계이며 상호 의존적이고 서로 간의 제약으로 인해서 둘 중 하나라도 없으면 제대로 된 힘을 발휘할 수 없다.
그럼 두가지 토큰은 어떤 관계를 가져야 하나?
1. 리프레쉬 토큰(옥새)은 액세스 토큰(왕)을 증명한다.(발급)
이 과정에서 국가(서버)는 필수적으로 옥새와 사용자를 검증해야 한다. 사용자가 가짜 옥새를 들고 온 상황이거나 정당한 사용자가 아닌 사람이 옥새를 들고 온 두 가지 경우 모두 정상적이지 않기 때문이다.
2. 액세스 토큰(왕)과 리프레쉬 토큰(옥새)의 역할은 구분되어야 한다.
완전한 권한을 둘로 나눈 것이기에 옥새만으로 왕명을 흉내 내거나 (서비스 접근)
왕이 옥새를 마음대로 찍어 내는 경우에는 옥새와 왕이 가진 정통성에 대한 견제가 무의미해지기 때문에
굳이 둘을 구분할 이유가 없을 것이다.
3. 액세스 토큰(왕)의 갱신을 위해서 리프레쉬 토큰(옥새)은 안전하게 보관되어야 한다.
왕이 힘이 있고 젊을 때는 사실 옥새는 별로 중요하지 않을 수 있다.
하지만 화무십일홍이라 결국 권력에는 끝이 있기 마련이라 왕이 늙고 힘이 약해졌을 때 다음 대 세자가 온전히 왕위를 잇고 강력한 왕권을 누리기 위해서는 옥새(정당성)는 필수적이다. 왕이 되고 싶은 사람은(해커) 언제나 출현할 수 있다.
그러니 안전하게 옥새를 보관하자.
대략적인 성질에 대해서는 간단히 설명이 끝났으니 이제 실제 구현에 대해서 간단히 정리해보자.
어떻게 구현해야 할까?
현재로써 보안상 비교적 안전한 구현 방법은
1. 액세스 토큰은 js 로컬변수(클로저 내부)에 보관하여서 사용자가 사용중일때만 유지하고 그 권한을 휘발하는게 가장 안전하다.
이럴 경우 이미 발급한 엑세스 토큰을 요청 시를 제외하고 탈취할 방법이 없기 때문이다.
2. 리프레쉬 토큰의 경우 클라이언트 측에서 접근이 불가능한 http only secure 쿠키(웹, 앱 차이)등에 저장하는 게 안전하다.
이건 사용자 입장에서 사용자 권한을 발급받기 위해서 리프레쉬 토큰에 접근할 필요가 없기 때문이다. 사용자는 로그인과 로그아웃 과정을 통해서 2가지 토큰을 발급받고 그를 통해서 액세스 권한을 얻으니 사실상 사용자 측에서 리프레쉬 토큰을 직접 관리하지 않는 게 보안적으로 유리하다.
그렇다고 직접적으로 관리하지 않는 것과 발급조차 받지 않는 것은 다르다.
액세스 토큰이 만료가 되었을때는 리프레쉬 토큰을 통해서 재발급 받을 수 있다.
그리고 만약 리프레쉬 없이 액세스 토큰 만으로 리프레쉬 토큰을 발급 받는게 가능하다면
액세스 토큰이 탈취 되었을때 그것이 발각 되기 전이라면 그 엑세스 토큰으로 리프레쉬 토큰을 발급받아서 그 리프레쉬 토큰으로 계속 엑세스 토큰을 재발급받을 수 있는 사실상 하나의 토큰으로 이루어진 구조가 되므로 AT와 RT를 사용하는 구조라면 그런 식으로 구현을 하는 것은 보안상 무의미하다고 볼 수 있다.
ex) AT 탈취 => AT로 RT 발급 => 발급된 RT로 AT 재발급 =>....
3. 토큰은 절대 로컬 스토리지에 보관해서는 안된다. js로 누구나 간단히 접근 가능하기에 xss 같은 스크립트 삽입 공격에 취약하다. 쿠키 역시 http only secure가 아니라면 취약하다.
또 csrf 문제로 인해서 서버 차원에서 해당 리프레쉬 토큰(또는 식별키)과 해당 토큰을 발급받은 사용자 데이터를 저장하고 있다가 액세스 토큰 발급 요청에서 해당 부분을 검증해야 한다.
위 3가지 방안을 지키는 게 비교적 안전하다.
당연한 말이지만 이렇게 해도 동탁(해커)에게서 절대적 안전을 보장할 수는 없긴 하다.
하지만 동네 양아치는 막을 수 있을 것이다.
'개발' 카테고리의 다른 글
ChatGPT에 대한 감상 (0) | 2023.01.21 |
---|---|
기술부채에 대하여. (0) | 2022.05.11 |
SOLID 원칙에 대하여 (0) | 2022.05.04 |
클린코드와 표준 (0) | 2022.04.28 |
개발 블로그 (0) | 2021.06.25 |