JWT와 Access Token, Refresh Token, RTR 방식 정리

728x90

JWT와 Access Token, Refresh Token, RTR 방식 정리


1. 쿠키, 세션 방식

기존의 사용자 인증 방식은 쿠키와 세션 방식으로 이루어졌다.

 

쿠키 방식은 Key, Value로 이루어진 구조로 만료시간, 도메인, 경로 등의 정보를  사용자의 로컬 하드에 저장하였다.

그러나 이는 개인 정보를 저장하는 것이기에 보안에 취약했으며, 용량 제한 때문에 많은 정보를 담을 수가 없었다.

 

이러한 문제를 해결하고자 사용자 정보를 서버 측에서 저장하고 관리하기 위해 세션 방식이 도입되었다.

세션 ID에는 중요한 정보가 담겨 있지 않지만, 세션 ID 자체를 탈취당하면 타인이 사용자인 척 위장할 수 있는 문제점이 있다.

또한, 사용자가 많아지면 서버에 트래픽 부하가 생기므로 성능이 저하되기도 한다.

 

이러한 단점을 극복하고자 JWT 방식이 도입되었다.

 

 

2. JWT(Json Web Token)

사용자가 로그인을 시도한 후 인증 처리가 되었다면 서버에서는 사용자에게 민감한 정보가 없는 데이터로 이루어진 토큰을 발급한다.

이후, 사용자는 해당 서비스를 이용할 때 마다 해당 토큰을 Header의 Authorization에 실어 보낸다.

서버는 클라이언트가 Header에 담아 보낸 JWT가 유효한 토큰인지 검사하는 과정을 거쳐야 한다.

Header와 Payload를 서버의 key값을 이용해 Signature를 만들고, 클라이언트가 보낸 Signature와 확인하여 일치하면 인가가 완료된다.

 

세션과 달리 서버가 아닌 클라이언트에 암호화된 토큰을 저장함으로써 서버 메모리의 부담을 줄일 수 있다.

 

 

2-1. JWT 구조

JSON 형태의 각 부분은 Base64로 인코딩 되어 표현된다.

각 부분은 '.' 구분자를 사용하여 구분된다.

 

헤더(Header)

토큰의 타입과 해시 알고리즘의 종류

{ "alg": "HS256", "typ": "JWT" }

 

페이로드(Payload)

어떤 상태 값이나 추가적인 정보가 담긴 부분. 미리 정의되어 있는 key - value 혹은 개발자가 임의로 정한 key - value를 정의하여 담을 수 있다. 이러한 한 쌍의 key - value를 Claim이라고 한다.

{"sub":"greenneuron@email.com","role":"ROLE_USER","iat":1674887738,"exp":1674974138}

sub: 토큰 발행 시 지정된 JWT의 주체(Principal)

iat: 토큰 발행 일시

exp: 토큰 만료 일시

role: 개발자가 임의로 추가한 claim

 

제 3자가 복호화하여 볼 수 있는 위험성이 있으므로 민감한 정보를 claim에 포함시키지 않는 것이 좋다.

 

시그니처(Signature)

토큰을 인코딩하거나 유효성 검증할 때 사용하는 고유한 암호화 코드.

헤더와 페이로드 값을 각각 BASE64로 인코딩 -> 인코딩 한 값을 비밀 키를 이용해 헤더에서 정의한 알고리즘으로 해싱 -> 다시 BASE64로 인코딩

 

생성된 토큰은 HTTP 통신을 할 때 Authorization이라는 key의 value로 사용되며, 일반적으로 value에는 Bearer가 앞에 붙여진다.

{ "Authorization": "Bearer {생성된 토큰 값}", }

 

2-2. JWT 장/단점

장점

데이터의 위변조 방지

사용자 인증에 필요한 정보는 토큰 자체에 포함하므로 서버가 필요없다.(Stateless)

확장성 우수

토큰 기반으로 다른 로그인 시스템에 접근 및 권한 공유가 가능

OAuth의 경우 소셜 계정을 통해 다른 웹서비스에 로그인이 가능하다.

모바일 환경에서도 기능한다.

DB를 사용하지 않으므로 인증이 빠르며 DB가 터져서 서버가 죽는 경우에도 작동할 수 있다.

 

단점

토큰의 길이가 길어 인증 요청이 많아질수록 네트워크 부하가 심해진다.

Payload 자체는 암호화가 되지 않아 중요한 정보는 담을 수 없다.

Payload 에 저장될 사용자 정보가 변경되면 payload에서 decode한 정보와 현재 정보가 일치하지 않게될 수 있다.

토큰 탈취에 대한 대처가 어렵다.

stateless 방식으로 인해 서버에서 토큰에 대한 탈취를 감지하고 제어하기가 어렵다.

 

 

2-3. Access Token과 Refresh Token

토큰이 탈취 당할 경우 서버에서는 이를 감지할 수 없기 때문에 이 두가지 토큰 방식에 유효시간을 할당하는 방법으로 보안성을 강화한다.

 

Access Token

사용자의 정보를 담고 있는 토큰으로, 사용자 인증/인가 용도이다. 서비스의 resource에 대한 접근을 가능하게 한다.

만약 탈취 당할경우 토큰이 만료되기 전까지 누구나 해당 토큰으로 권한 접근이 가능해지는 문제점을 가지고 있다.

따라서 만료시간을 짧게 잡는다.

 

Refresh Token

Access Token이 만료되면 새로운 토큰을 발급해주는 역할을 한다. 따라서 Access Token보다는 만료시간을 더 길게 잡는다.

Refresh Token이 없다면 사용자는 토큰이 만료될 때 마다 새로 로그인을 하여 토큰을 재발급 받아야 할 것이다.(로그인 여러번 안하게 함)

또한, Refresh Token은 서버의 DB에 저장하는 방식이므로 탈취되었음을 확인하면 해당 Refresh 토큰을 곧바로 폐기시킬 수 있는 장점이 있다.(탈취 상황 발생시 제어 용이하게 함)

 

Access Token과 Refresh Token 작동 방식

 

1. 사용자가 로그인 시도 또는 인증 서버에 요청을 한다.

2. 서버는 로그인 성공시 AT와 RT를 사용자에게 반환한다. 서버는 RT에 대한 정보를 DB에 저장한다. 사용자는 쿠키나 세션에 AT, RT를 저장한다.

3. 사용자가 헤더에 AT를 실어 요청을 보낸다. -> 서버가 검증 처리후 문제가 없으면 요청에 대한 응답을 보낸다.

4. 만약 사용자가 만료된 AT 요청을 보낼 경우 -> 서버는 401(Unauthorization: 권한없음)에러를 보낸다.

 

* AT의 Payload에 토큰의 유효기간이 나타나 있으므로, 프론트 측에서 api 요청 전에 토큰이 만료되었음을 확인하면 바로 재발급 요청을 서버에 보내어 서버의 토큰 검증 과정을 건너뛰게 구현할 수도 있다.

 

5. 사용자 측에서 401에러를 확인하면 Authorization 서버에 대해 AT, RT를 둘 다 보낸다.

6. Authorization 서버는 먼저 AT에 대한 검증을 한다. 그 후, DB에 보관했던 RT와 요청이 들어온 RT값을 비교하여 유효기간이 지나지 않았고 두 값이 동일할 경우 새로운 AT를 발급하여 사용자에게 보낸다.

 

 

3. RTR(Refresh Token Rotation)

위의 방식에서 만약 AT와 RT가 모두 만료가 되었다면 사용자는 새로 로그인을 해서 새로운 토큰들을 발급받는 수 밖에 없다. 게다가 두 토큰 모두 서버가 관리하지 않는 stateless 상태이므로 탈취를 당하더라도 만료시간이 다 되도록 기다려야 하는 큰 문제가 발생한다.

 

RTR 방식은 RT를 사용할 때마다 새로운 AT와 RT를 발급받는 방식으로 이를 통해 RT는 한번만 사용할 수 있도록 구현된다.

RT를 재발급 할 때마다 기존에 DB에 저장해두었던 RT또한 갱신해야하기 때문에, 이 과정에서 RT가 탈취되었을 때 서버측에서도 인지가 가능해진다.

 

 

 

그러나 문제는 탈취범이 사용자보다 먼저 새로운 RT를 rotation할 때 벌어진다. AT는 탈취당하더라도 비교적 유효기간이 짧기 때문에 크게 부담이 되지 않지만, RT는 그보다 유효기간이 길 뿐만아니라, 새로운 RT가 사용되면 AT와 RT가 새로 발급이 되기 때문에 DB에는 탈취범에 의해 새로 생성된 RT값이 들어있는 상태가 될 것이다.

이때 사용자가 RT를 사용하면 사용자가 보내준 RT와 DB에 들어있던 RT값이 다르기 때문에 졸지에 내가 탈취범이 되어버린 것 마냥 서비스를 이용할 수 없게 되어버린다... 심지어 탈취범은 탈취한 RT를 통해 새로운 RT, AT를 지속적으로 재발급 받아 사용할 수도 있다.

 

*중간자 공격: 사용자의 인터넷 서버와 인터넷 트래픽 목적지 사이에 끼어들어 데이터 전송을 가로채는 기법.

 

 

따라서 이를 방지하려면 한 번 이상의 RT 사용이 감지되면 그 이후에는 탈취된 것으로 간주하고 새로운 RT, AT 발급 이후에 기존 RT를 폐기하는 조치를 취할 수 있다.

 

 

RT가 탈취되었더라도 한 번 이상의 RT 사용이 감지되면 AT, RT를 모조리 새로 발급하여 기존 RT가 사용되지 못하도록 하는 것이다.(RT를 일회용으로 만듬)

그 외, 중간자 공격을 예방하기 위해서는 VPN 사용, HTTPS만 사용, 모든 통신에 종단간 암호화 실시 등의 방법이 있다.

 

물론 탈취자가 사용되지 않은 RT를 탈취하거나 지속적으로 AT를 탈취하는 경우는 예방할 수 없다.

 

 

 

4. 토큰 저장 장소

서버에서는 주로 DB 혹은 Redis와 같은 NoSQL에 토큰을 저장한다.

그러나 DB에서 RT를 관리하는 경우, 토큰에 대한 TTL(Time to Live)를 지정하기도 적합하지 않고, 매번 검증 때마다 쿼리문을 날려야 하기 때문에 대부분의 블로그에서는 Redis에서 관리하는 방식을 활용하는 것 같다.

 

클라이언트에서는 http-only 속성이 부여된 쿠키에 저장하는 것을 권장한다. 해당 속성이 부여된 쿠키는 자바스크립트 환경에서 접근할 수 없기 때문에 XSS, CSRF가 발생하더라도 토큰이 누출되지 않는다고 한다.

 

*XSS: 보안이 취약한 웹 사이트에 악의적인 스크립트를 넣어놓고 사용자가 이 스크립트를 강제로 실행하게 유도하여 사용자의 AT를 탈취하는 기법

*CSRF: 사용자가 자신의 의지와는 무관하게 탈취자가 의도한 행위를 특정 웹 사이트에 요청하게 만드는 기법

728x90