기존 사내 프로젝트는 session방식으로 인증 구현을 해왔다.
현재 유지보수하고 있는 프로젝트에서 추후 확장성을 이유로 session에서 JWT으로 인증 방식을 바꾸게 되었다.
session과 JWT 구현 방식이 무엇이고 어떤 차이가 있는지 정리하고, 발생했던 동시성 이슈와 해결 방법에 대해 정리해보고자 한다.
Session
세션 방식이 인증되는 방식은 아래와 같다.
- 클라이언트가 인증정보(email, password)를 서버에 전송
- 서버는 메모리에 사용자를 저장하고 Session Id를 클라이언트에 쿠키로 전달
- 클라이언트는 쿠키에 저장된 Session Id를 사용해 요청
- 서버는 일치하는 Session Id를 메모리에서 검색한 후 있다면 권한 부여
Session 방식의 장점
- Session Id를 서버에서 관리하기 때문에 보안성이 높다.
- 사용자 인증 정보가 브라우저에 저장되지 않으므로, 쿠키 탈취 등의 보안 위협에 취약하지 않다.
혹시 SessionID가 탈취되더라도 DB에 저장되어 있는 Session을 삭제하면 끝!
Session 방식의 단점
- 세션을 DB에 저장해서 탐색하기 때문에 유저가 늘어날수록 서버의 부담이 증가한다.
- 여러 개의 프로세스를 돌리거나 서버 컴퓨터를 추가하는 것이 어려워진다.
JWT
JWT(Json Web Token)는 서로 간의 정보를 JSON 개체로 안전하게 전송하기 위한 간결하고 자체 포함된 방법을 정의하는 개방형 표준(RFC 7519)이다. 간단하게, "의미가 있는" 문자열 토큰이다.
JWT의 구조
JWT는 3개의 점으로 구분된 세 부분으로 구성된다.
- Header: 토큰 타입과 어떤 알고리즘이 쓰였는지를 나타냄
- Payload: 사용자 및 정보가 담김. payload 정보는 인코딩만 되어있기 때문에 누구라도 디코딩하여 확인 가능
- Signature: 인코딩된 header, 인코딩 된 payload, secret값을 합친 뒤 지정된 알고리즘으로 해싱
JWT 방식이 인증되는 방식은 아래와 같다.
- 클라이언트가 인증정보(email,password)를 서버에 전송(동일)
- 서버는 Secret 정보를 이용해 JWT를 생성하여 클라이언트에게 전달
- 클라이언트는 JWT를 이용해 요청을 보냄
- 서버는 JWT가 일치하는지 decoding하여 확인 후 권한 부여
JWT 방식의 장점
- 무상태와 확장성: token은 클라이언트 쪽에만 저장되므로 stateless 하며, 서버를 확장하기에 적합한 환경을 제공해 준다.
- 토큰을 사용해 다른 서비스에도 권한을 공유할 수 있다.
JWT 방식의 단점
- JWT 토큰이 탈취되었을 경우, 누구나 해당 토큰의 권한을 가지고 서비스를 이용할 수 있게 된다.
- 누구나 헤더와 페이로드의 내용을 볼 수 있기 때문에 보안에 위협이 될만한 정보나 개인정보가 있다면 노출될 수도 있다.
- 토큰에 담고 있는 정보가 많아질수록 데이터의 크기가 커져 네트워크 전달 시 데이터의 크기로 부하가 생길 수 있다.
위와 같은 이유로, 보통 jwt 토큰(accessToken)의 만료시간(exp)은 짧게 정하고 refreshToken과 함께 사용한다.
refreshToken은 DB에 저장되며 긴 만료시간을 가진다.
사용자의 accessToken이 만료되면 refreshToken을 이용해 갱신하여 사용하게 된다. 우리가 별도의 재로그인 없이 서비스를 계속 이용할 수 있는 것은 refreshToken 덕분이다.
비교적 만료시간이 짧은 accessToken은 탈취되어도 금방 만료되기 때문에 보안상 위협이 비교적 적은데 반해 refreshToken은 새로운 jwtToken을 발급받을 수 있기 때문에 탈취되면 위험하다.
따라서 이 경우에는 token의 payload를 확인해 userId를 block 시키는 방법, 해당 유저의 refreshToken을 모두 만료시키는 방법이 있다.
웹에서의 구현
Session
Session 방식의 경우 저장 방식에 따라 다르겠지만 웹에서 따로 처리해 줄 건 없다.🙈
로그인 요청 시 서버 측에서 httponly& secure 쿠키로 저장되고 이후 요청을 보낼 때 헤더에 sessionId가 실려서 보내진다.
intercepter에서 sessionId가 만료될 때 로그인 페이지로 이동시켜 주면 될 것이다.
JWT
JWT 방식의 경우 accessToken이 만료되었을 때 동시에 여러 API 요청이 이루어질 경우 동시성 이슈가 발생할 수 있다. 다시 말해, 여러 API 요청이 동시에 accessToken 만료를 감지하고 각각 refreshToken을 이용해 accessToken을 갱신하려고 시도할 때 동시성 문제가 발생한다.
동시성 문제
프로젝트에서는 Axios에서 응답 인터셉터를 통해, 응답 인터셉터에서 401 응답을 감지했을 때 토큰 갱신 로직을 실행해 주고, 갱신 중 다른 요청이 들어오면, 이 요청들은 refreshSubscribers에 큐로 추가한 뒤, 토큰 갱신 완료 후 재시도해주었다.
(만약 토큰 갱신에 실패하거나 403 응답이 반환되면 로그인 페이지로 리디렉션 시켜주었다.)
Session? JWT? 무엇을 선택해야 할까
서비스에 따라 다르다.
Session 방식의 경우, 현재 DB에 로그인된 Session Id를 트레킹 할 수 있기 때문에, 다른 기기 로그아웃 기능을 통해 다른 기기에 로그인된 계정을 로그아웃 시킬 수도 있다. 또는 넷플릭스 같이 로그인 계정 수를 N개로 제한하는 기능도 구현 가능하다.
반대로 JWT의 경우, 별도의 인증 저장소가 필요하지 않기 때문에 서버 부하를 낮출 수 있으며, 분산/클라우드 기반 인프라 스트럭처에 잘 대응할 수 있다.
참고:
https://hayeon1549.tistory.com/38
'Front-end > Web' 카테고리의 다른 글
[Web] 브라우저와 동작 방법(렌더링, DOM) (0) | 2022.11.10 |
---|