JWT
프로젝트 중에 JWT를 사용하여서 잘 몰라서.. 공부하고자
HTTP 프로토콜은 웹에서 클라이언트와 서버 간의 통신을 가능하게 하는 기본적인 규약이다.
그러나 이 프로토콜은 connectionless(연결이 끊어짐)하고 stateless(상태 비저장) 특성을 갖는다. 즉,
한 번의 통신이 끝나면 연결이 끊어지고, 이후 요청에서 이전 상태를 기억하지 않기 때문에 매번 사용자 인증과정을 반복해야 한다는 단점이 있다.
전통적인 세션 관리 방식은 서버에 사용자의 상태를 저장하고, 클라이언트가 쿠키를 통해 세션 ID를 전송하여 이를 식별한다.
그러나 이 방식은 사용자가 많아질 경우 서버에 부하를 줄 수 있고, 여러 서버를 사용할 때는 세션 공유 문제가 발생할 수 있다.
그러던 중 RESTful API와 같은 상태 비저장(stateless) 아키텍처의 필요성이 커지면서, 서버가 클라이언트의 상태를 관리하지 않고도 인증을 처리할 수 있는 방식이 필요해 졌다.
그때, JWT가 등장하였다.
JWT는 사용자 정보를 포함하는 JSON 형식의 토큰으로, 클라이언트가 서버에 요청할 때 이 토큰을 함께 전송한다.
이 토큰은 서버에서 서명되어 있어, 중간에 변경되지 않았음을 확인한다..
덕분에 서버는 상태를 저장할 필요 없이, 클라이언트가 보낸 토큰을 검증하기만 하면 사용자 인증을 수행할 수 있다.
JWT를 통해 사용자 정보를 확인할 수 있고, 다양한 플랫폼과 언어 간의 호환성도 좋다.
JWT (JSON Web Token)
정보를 JSON 형태로 클라이언트에 저장하고 서버는 해당 토큰을 검증하여 인증을 처리하는 방식이다.
토큰 안에 미리 인증에 필요한 정보를 넣어두면 매 요청마다 서버와 통신할 필요가 없다. 이로 인해 서버 부하와 같은 문제를 해결할 수 있다.
JWT를 활용한 로그인 프로세스
1. [클라이언트의 인증 요청]
사용자가 아이디와 비밀번호를 입력하여 서버에 인증 요청을 보낸다.
2. [사용자 정보 유효성 확인]
서버는 데이터베이스와 비교하여 사용자의 정보가 유효한지 확인한다.
3. [서버 : JWT 생성]
인증에 성공하면 서버는 사용자의 정보를 기반으로 JWT를 생성한다.
4. [클라이언트에 JWT 반환]
서버는 생성된 JWT를 클라이언트에 반환한다.
5. [JWT 저장]
클라이언트는 받은 JWT를 세션이나 쿠키에 저장한다.인증 필요한 자원에 접근시
6. [인증이 필요한 API 요청]
인증이 필요한 API를 호출할 때, Authorization헤더에 JWT를 포함시킨다.
7. [서버에서 JWT 검증]
서버는 요청을 받으면 JWT를 검증한다.
8. [리소스 제공]
JWT 가 유효한 경우 서버는 보호된 자원을 사용자에게 반환한다.
JWT 구조
헤더, 페이로드, 서명 세 부분으로 구성된다.
페이로드는 클레임이라고 불리는 정보 조각을 포함한다.
클레임은 토큰에 추가할 정보를 의미하며 사용자 ID, 권한 정보등이 포함된다.
하지만 디코딩을 통해 들어있는 정보를 확이할 수 있기 때문에 민감한 정보는 포함하지 않는 것이 좋다.
서명은 토큰을 인코딩하거나 유효성 검증을 위해서 사용되는 부분으로, 헤더와 페이로드를 합친 후 비밀키를 사용하여 생성한다.
헤더
- 헤더는 JWT의 메타데이터를 포함하며 일반적으로 토큰의 타입과 사용된 알고리즘 방식을 나타낸다.
- 예를 들어, {“alg”: “HS256”, “typ”: “JWT”}와 같이 설정됩니다.
페이로드
- 실제 데이터가 담긴 부분으로 여러 개의 클레임을 포함한다.
- 클레임은 JWT에 담을 정보 조각으로 사용자ID, 권한, 만료 시간 등이 포함된다.
- 쉽게 디코딩될 수 있기때문에, 민감한 정보(pw)는 포함하지 않는 것이 좋다.
서명
- 헤더와 페이로드를 합친 후 비밀키를 사용해 생성된다.
- 토큰의 무겴성을 검증하는 용도로 사용된다.
- 서버는 클라이언트로부터 받은 토큰을 검증하기 위해 서명 부분을 해싱 알고리즘과 비밀키로 다시 생성하고 이를 클라이언트가 전달한 서명과 비교하여, 일치하면 인증이 유효한 것으로 판단한다.
이러한 구조로 JWT는 클라이언트와 서버 간의 인증 정보를 안전하게 전송하고, 서버는 상태를 저장하지 않고 사용자를 인증할 수 있다.
JWT는 발급될 때 유효기간을 설정하는데, 한 번 발급된 JWT는 그 유효기간이 만료될 때까지 변경할 수 없다.
따라서 유효기간이 짧게 설정되면 로그인을 계속 해야해서 사용자에게 불편을 줄 수 있고, 길게 설정하면 보안이 취약해질 수 있다.
그리고 JWT는 서버에서 상태를 관리하지 않기 때문에, 사용자가 로그아웃하거나, 토큰을 무효화하고자 할 때 즉시 처리하기가 어렵다.
또한 서명되어 있기 때문에 데이터의 변조는 방지할 수 있지만, JWT는 만료될 때까지 사용이 가능하기 때문에, 유효기간이 긴 토큰이 탈취되는 경우 위험하다.
이러한 단점을 보완하기 위해 Access Token과 Refresh Token이 같이 사용된다.
Access Token과 Refresh Token
Access Token
Access Token은 사용자가 서버에 요청할 때 전달되는 토큰으로, 짧은 유효기간을 갖는다.
이 토큰은 사용자의 인증과 권한을 검증하는 데 사용되며, 토큰이 만료되면 클라이언트는 더 이상 이 토큰으로 요청할 수 없다.
그러나 유효기간이 짧기 때문에 탈취된 경우에도 큰 피해를 줄 수 있다.
Refresh Token
Refresh Token은 Access Token의 유효기간이 만료되었을 때 새로운 Access Token을 발급받기 위한 토큰으로 유효기간이 더 길고, 민감한 정보가 포함되지 않는다. Access Token이 만료되었을 때 클라이언트는 Refresh Token을 서버로 보내 Access Token을 새로 발급받는다.
Access Token과 Refresh Token의 흐름
1. 로그인 시 Access Token과 Refresh Token 발급
2. 사용자가 로그인하면, 서버는 클라이언트에게 두 개의 토큰을 발급한다.
- 짧은 유효기간을 가진 Access Token
- 긴 유효기간을 가진 Refresh Token
3. Access Token을 사용한 요청
사용자는 Access Token을 가지고 API 서버에 요청을 보내며, 서버는 Access Token을 검증하여 유효하면 요청을 처리한다.
Access Token 만료 시 Refresh Token을 이용해 Access Token을 갱신한다.4. Access Token 발급
Access Token이 만료되면 클라이언트는 Refresh Token을 서버에 전달하여 새로운 Access Token을 요청한다.
서버는 Refresh Token을 검증한 후, 새로운 Access Token을 발급해 클라이언트에 반환한다.5. 로그아웃 또는 Refresh Token 만료
사용자가 로그아웃하면, 서버는 Refresh Token을 무효화하는 로직을 실행한다. (블랙리스트)
만약 Refresh Token의 유효기간이 만료되면, 사용자는 다시 로그인을 통해 새로운 Refresh Token을 받아야 한다.
Refresh Token을 사용하는 이유
Access Token을 짧은 유효기간을 갖도록 해서, 탈취되더라도 그 피해를 최소화할 수 있다. 하지만 짧은 유효기간으로 인해 Access Token이 자주 만료되기 때문에, 매번 재발급받야아 하는 번거로움이 있다. Refresh Token은 더 긴 유효기간을 가지며, 민감한 정보는 포함하지 않는다. 그래서 서버는 Access Token이 만료되었을 때, Refresh Token을 사용하여 새로운 Access Token을 발급한다.
이를 통 짧은 유효기간의 Access Token으로 보안을 강화하고, 긴 유효기간의 Refresh Token을 사용하여 사용자 편의성을 유지할 수 있다.
Refresh Token Rotation
리프레시 토큰은 한 번 발급되면 장기간 사용할 수 있는 토큰으로, 한 번 탈취되면 계속해서 엑세스 토큰을 발급할 수 있다.
이 위험을 줄이기 위해 리프레스 토큰을 한 번 사용하면, 다시 새로운 리프레시 토큰을 발급하고 이전 토큰을 무효화하도록 한다. 이 방식이 Refresh Token Rotation 이다.
RTR 흐름
1. 액세스 토큰과 리프레시 토큰 발급
사용자가 처음 로그인하면 서버는 액세스 토큰과 리프레시 토큰을 클라이언트에게 발급한다.2. 액세스 토큰 재발급
액세스 토큰이 만료되면, 클라이언트는 리프레시 토큰을 사용하여 새로운 액세스 토큰을 요청한다. 이때, 서버는 클라이언트에게 새로운 액세스 토큰과 새로운 리프레시 토큰을 함께 발급해준다.3. 이전 리프레시 토큰을 무효
새로운 리프레시 토큰이 발급되면 이전 리프레시 토큰은 즉시 무효화되며, 한 번 사용된 리프레시 토큰은 다시 사용할 수 없다.4. 탈취 방지 리프레시 토큰이 탈취된 경우, 공격자는 그 토큰을 사용하려고 시도한다.
하지만, 리프레시 토큰이 한 번 사용될 때마다 새로운 토큰으로 교체되기 때문에, 이미 무효화되어 사용할 수 없다. 따라서, 공격자는 탈취한 토큰으로 다시 액세스 토큰을 발급받을 수 없다.5. 서버 측 저장 관리 서버는 클라이언트에게 마지막으로 발급된 리프레시 토큰만을 저장하고 관리한다. 따라서 새로 발급된 리프레시 토큰만 유효하고, 이전의 모든 리프레시 토큰은 사용할 수 없다.