CS/네트워크

JWT

H.E 2024. 2. 4. 15:20

[ JWT 사용하는 이유 ]

▶ Cookie

클라이언트가 웹 사이트에 접속할 때 그 사이트가 사용하게 되는 일련의 작은 기록 파일이라고 생각하면 됨

서버가 클라이언트에 정보를 전달할때 저장하고자 하는 정보를 응답 헤더(Cookie)에 저장하여 전달

Key-Value 형식의 문자열 형태로 저장됨

 

Set-Cookie에 던진 쿠키를 확인할 수 있음

쿠키는 로그인을 했을때 Set-Cookie의 형태로 반환을 받은 쿠키를 토대로 로그인이 필요한 요청을 할 때마다 받은 쿠키를 던져 요청을 하는 동작 구조를 가짐

로그인이 필요한 요청마다 ID와 PW를 보내야했다면 매우 번거로웠을태지만 이것을 쿠키로 대체한것

 

Cookie 단점

  • 쿠키는 노출이 되었을 때 id, pw에 대한 민감 정보까지 다 노출이 되어 보안이 좋지 않음
  • 조작당해서 들어올 가능성이 있음
  • 웹 브라우저마다 쿠키에 대한 지원 형태가 다르기 때문에 다른 브라우저간의 공유가 불가능함
  • 쿠키의 사이즈가 제한(4KB) 되어있어 원하는 만큼의 충분한 데이터를 담을 수 없을 경우가 있음
  • 서버는 매번 id pw를 받아서 인증을 해야 하는 불편함이 있으며 조작된 데이터가 넘어오는 경우를 방지할 수 없음

 

▶ Cookie & Session

쿠키만 사용했을때 개인 민감정보를 그대로 노출하는 단점을 개선하기 위해 Session을 같이 사용함

무언가에 대한 특정 인증 정보를 서버가 가지고 있고 그 값을 클라이언트에게 전달하여 마치 키를 주고 자물쇠를 여는 방식으로 인증을 함. 이때 세션 ID를 특정 저장소에 저장하여 사용함
즉, 새로운 성능의 기술 스펙을 구현한 것이 아니라 id pw라는 민감정보를 다뤄야 하는 로그인과 같은 상황에서의 보완점을 찾기 위 해서 나온 것이 Session인것

 

쿠키와 같은 매개체를 주고받음으로써 "매번 로그인 시 id pw를 작성해서 전달해야 한다"에 대한 단점을 보완하자는 기본적인 틀이고, 여기서 id pw를 주고받지 말고 인증 정보 자체를 특정 세션 저장소에 저장하고, 이 값을 쿠키에 담아 클라이언트가 쿠키를 요청할 때마다 세션 저장소에 있는 정보랑 동일한지로 로그인을 확인하자는 것이 주요 핵심

 

Cookie & Session 인증 과정

  1. 클라이언트가 id pw로 서버에 로그인 요청
  2. id pw로 인증 후 사용자를 식별할 특정 유니크한 세션 ID를 만들어 마치 자물쇠처럼 서버의 세션 저장소에 저장
  3. 세션 ID를 특정한 형태(쿠키 or json)로 클라이언트에 다시 반환
  4. 이후 사용자 인증이 필요한 정보를 요청할 때마다 이 세션 ID를 쿠키에 담아 서버에 함께 전달
  5. 인증이 필요한 api일 때, 서버는 세션 ID가 세션 저장소에 있는지 확인
  6. 있다면 인증 완료 후 api 처리, 없다면 401 에러를 반환

 

Cookie & Session 단점

  • 세션 저장소의 문제가 발생하면 인증 체계가 무너져 이전에 다른 인증된 유저 또한 인증이 불가해짐
  • stateful하기 때문에 http의 장점을 발휘하지 못하고 scale out에 걸림돌이 생김
    • stateful: 현재 상태(state)를 기억하고 유지하는 특성
    • scale out: 애플리케이션의 성능을 향상시키기 위해 서버 또는 리소스를 추가하는 것
  • 세션 저장소가 필수적으로 존재하기 때문에 이를 사용하기 위한 비용이 듬
  • 세션 ID가 탈취되었을 경우 대처는 가능하지만 클라이언트인척 위장하는 보안의 약점이 있을 수 있음
  • 사용자가 많아질수록 메모리를 많이 차지하게 됨
  • "매번" 요청 시 세션 저장소를 조회해야 함
    • 요청을 진행할 때마다 세션 저장소에 세션 ID를 조회하는 작업을 통해서 DB 접근이라는 로직이 한번 더 수행됨

 

[ JWT 등장 ]

JWT(Json Web Token)은 인터넷 표준 인증 방식. 인증에 필요한 정보들을 Token에 담아 암호화 시켜 사용하는 토큰

기본적인 인증을 진행하는 구조는 Cookie때와 크게 다르지 않지만 JWT는 서명된 토큰이라는 점이 다름

공개/개인 키를 쌍으로 사용하여 토큰에 서명할 경우 서명된 토큰은 개인 키를 보유한 서버가 이 서명된 토큰이 정상적인 토큰인지 인증할 수 있음. 이러한 구조로 인해 인증정보를 담아 안전하게 인증을 시도하게끔 전달할 수 있음

 

▶ JWT 구성

aaaaaaa.bbbbbbb.ccccccc //헤더.페이로드.시그니처

 

구성은 각각의 구성요소가 점(.)으로 구분이 되어있으며 헤더, 페이로드, 시그니처로 나뉨

 

각 역할

  • 헤더: 토큰 타입, 암호화 알고리즘 명시
  • 페이로드: JWT에 넣을 데이터, JWT 발급 / 만료일 등 명시
    • payload에 민감한 정보를 담으면 안됨
    • json으로 디코딩 되어있을 뿐 특별한 암호화가 걸려있는 것이 아니기 때문에 누구자 jwt를 가지고 디코딩을 한다면 header나 payload에 담긴 값을 알 수 있기 때문에 식별을 하기 위한 정보만을 담아야 함
  • 시그니처: 헤더, 페이로드가 변조 되었는지를 확인하는 역할
    • JWT 토큰을 클라이언트가 서버로 요청과 동시에 전달함
    • 서버가 가지고 있는 개인키를 가지고 Signature를 복호화한 다음 header값이 일치하는지 payload와 일치하는지 확인하여 일치한다면 인증을 허용

 

장점

  • 토큰 자체가 인증된 정보이기 때문에 세션 저장소와 같은 별도의 인증 저장소가 "필수적"으로 필요하지 않음
  • 세션과는 다르게 클라이언트의 상태를 서버가 저장해 두지 않아도 됨
  • signature를 공통 기 개인키 암호화를 통해 막아두었기 때문에 데이터에 대한 보완성이 늘어남
  • 다른 서비스에 이용할 수 있는 공통적인 스펙으로써 사용할 수 있음

 

단점

  • 쿠키, 세션때와 다르게 base64 인코딩을 통한 정보를 전달하므로 전달랴이 많아 네트워크 전달시 많은 데이터 양으로 부하가 생길 수 있음
  • Payload에는 암호화가 되어있지 않기 때문에 민감한 정보를 저장할 수 없음
  • 토큰이 탈취당하면 만료될 때까지 대처가 불가능

 

 

여기서 제일 크리티컬한 문제는 토큰이 탈취당하면 만료될 때까지 대처가 불가능하다는 점

세션에 경우에는 탈취 당했다고 판단이 되면 세션 저장소에 그 값을 지워 탈취된 후의 상황을 보완할 수 있지만 그것은 서버에서 클라이언트의 상태를 저장하는 stateful한 상황이기 때문

토큰의 경우는 statless하기 때문에 탈취가 되었다고 판단되어도 서버가 관리할 수 있는 방법이 없음

 

[ JWT토큰 단점 보완 ]

만료시간을 짧게 가져가면 됨. 짧은 시간만 해당 토큰이 유효하기 때문에 최소한의 보안성을 보장할 수 있음

하지만 유저가 짧은 시간이 지나 새롭게 로그인을 다시 해야한다면 불편한 경험이기 때문에 이 불편한 경험을 개선시킬 방법이 필요함

 

▶ 짧은 만료시간에 대한 사용자 불편함 보완

Sliding Session

특정한 서비스를 계속 사용하고 있는 특정 유저에 대해 만료시간을 연장시켜주는 방법

 

어떠한 유저가 로그인이 필요한 기능을 사용하고 있는데 도중에 인증이 만료되어 해당 기능이 취소되는 것은 아주 불편한 경험임 따라서 글쓰기, 결제 등과 같은 특정 action을 유저가 행동하였을 때 새롭게 만료시간을 늘린 JWT를 다시 제공함으로써 만료시간을 연장하여 보완하는 방법

 

하지만 접속이 단발성으로 일어난다면 Sliding Session으로 연장시켜줄 수 없는 상황이 생기고, 너무 긴 Access Token을 발급시켜준 상황이라면 Sliding Session 때문에 무한정 사용하는 상황이 발생할 수 있음

 

Refresh Toekn

가장 많이 사용하는 방법으로 JWT를 처음 발급할 때 Access Token과 함께 Refresh Token이라는 토큰을 발급하여 짧은 만료시간을 해결하는 방법


짧은 시간이 아닌 비교적 긴 시간의 만료시간을 가진 Refresh Token은 말 그대로 Access Token을 Refresh 해주는 것을 보장하는 토큰. 만약 클라이언트가 Access Token이 만료됨을 본인이 인지하거나, 서버로부터 만료됨을 확인받았다면 Refresh Token으로 서버에게 새로운 Access Token을 발 급하도록 요청하여 발급받는 방식

 

Refresh Token이 동작하는 방식

  1. 클라이언트가 ID, PW로 서버에게 인증을 요청하고 서버는 이를 확인하여 Access Token과 Refresh Token을 발급
  2. 클라이언트는 이를 받아 Refresh Token를 본인이 잘 저장하고 Access Token을 가지고 서버에 자유롭게 요청
  3. 요청을 하던 도중 Access Token이 만료되어 더 이상 사용할 수 없다는 오류를 서버로부터 전달
  4. 클라이언트는 본인이 사용한 Access Token이 만료되었다는 사실을 인지하고 본인이 가지고 있던 Refresh Token를 서
  5. 버로 전달하여 새로운 Access Token의 발급을 요청
  6. 서버는 Refresh Token을 받아 서버의 Refresh Token Storage에 해당 토큰이 있는지 확인하고, 있다면 Access Token
  7. 을 생성하여 전달.
  8. 이후 2로 돌아가서 동일한 작업을 진행


Refresh Token Storage 서버에서 Refresh Token을 저장하는 저장소

어떻게 보면 세션 저장소와 똑같은 역할을 한다고 생각해도 됨. 만료된 Access Token은 더 이상 인증의 역할을 못하니 Refresh Token을 서버로 전달하고, 이 Refresh Token 이 서버의 token 저장소에 있는지 확인 후 Access Token을 발급하는 형태


사실상 세션과 별반 차이 없이 특정 Storage에 I/0 작업이 발생하게 되기 때문에 Access Token이 지속되는 짧은 시간 동안만 I/0 작업이 일어나지 않는다이지, 세션의 단점을 하나 가져가는 셈지만
 이를 사용함으로써 세션처럼 토큰 자체가 탈취되었다고 판단이 뒤면 Refresh Token Storage를 초기화하여 탈취 된 토큰이 더 Refresh 못하도록 막는 등과 같은 부가 옵션이 생김


세션 방법과 같다고 생각할 수 있지만 이점이 명백히 있음

세션은 항상 인증 요청을 할 때마다 세션 ID를 세션 저장소에 있는 세션 ID에 비교를 해야 함. 만약 30분에 1만 번의 요청을 한다고 했을 때 I/O는 1만 번 작동 하지만 Refresh Token은 Refresh 하는 그 순간 즉, 요청을 했는데 access token이 만료되었을 때만 I/0 작업을 함. 30분에 1만 번의 요청을 한다고 했을 때 1만 번 access token으로 I/0 작업 없이 요청을 주고받고 30분이 지나면 딱 한번 Refresh 하기 위해서 요청을 하는 것

'CS > 네트워크' 카테고리의 다른 글

JWT - 어떻게 사용해야하는가?  (1) 2024.03.06
Socket  (0) 2024.02.04
HTTP2/HTTP3/웹소켓  (2) 2023.11.25
웹의 동작 원리  (0) 2023.11.12
DNS  (0) 2023.11.12