JWT의 모든 것

NaHyun_kkiimm·2024년 3월 7일

목록 보기
1/2

내가 아는 JWT의 모든 것을 정리해봤습니다. 마지막에는 공부하면서 궁금했던 질문들 정리했습니다. 참고해주세요~

사전 지식

1. HTTP의 비연결성와 비상태성

비연결성 (Connectioneless)
: 요청과 응답을 한 번 주고 받으면, 바로 연결을 끊어버리는 HTTP의 특성

비상태성 (Stateless)
: 요청과 응답을 교환하는 동안 상태(state)를 저장하지 못하는 HTTP의 특성
따라서, HTTP 레벨에선 이전에 보냈던 요청과 응답을 기억하지 못한다.

Q) 페이지를 이동할 때마다 새로운 로그인을 계속 해줘야하는가?
: 위의 특성대로라면, 사용자는 새 페이지를 로딩하거나 이동할 때마다 계속해서 로그인을 요청해야하지만, 우리는 그렇게 사용하지 않고 있다. 어떻게 상태를 유지하는가?

ConnectionlessStateless를 보완하기 위해 CookieSession을 이용한다.


인증 방식

1. 세션 기반 인증 방식

: 사용자의 인증 정보가 서버의 세션 저장소에 저장하는 방식

[ 장점 ]

  • 서버에 저장하기에 보완상으로나 관리상에서 매우 편하고 효율적

[ 단점 ]

  • DB 과부하 : 서버에서 클라이언트 상태를 모두 유지하고 있어야하기에, 클라이언트 수가 많아지면 메모리나 DB에 부하가 심해진다
  • 멀티 디바이스 환경(모바일, 공동 사용 등) : 로그인 시 중보 로그인 처리가 되지 않는 등의 신경 써야할 부분이 많다.
  • 사용자가 많아질 경우 로드 밸런싱을 사용한 서버 확장을 이용해야 하는데 이 때 세션의 관리가 어려워진다.
  • 쿠키로 저장된 Session ID에는 사용자의 정보(이름, 이메일 등)의 정보가 포함되어 있지 않기 때문, 해당 정보를 원한다면 조회를 다시 해야한다.
    • 서버에 접속할 때마다, Session Table에 접근해야 한다.
    • 사용자가 새로운 장치로 접속할 때마다, Session ID를 새롭게 생성해야 한다.

[ 동작 원리 ]

(1) Browser → App Server : Login 시도
(2) App Server → DB : 회원이 있는지 조회
(3) DB → App Server : 해당 Session ID 반환

  • 있다면, Session Table에서 해당 User ID의 Sessoin ID를 반환한다.
  • 없다면, Session 생성

Session Table
: 사용자 세션을 관리하기 위해 서버에서 사용하는 테이블

Session IDUser ID
abc12323

(4) App Server → Browser : 해당 Session ID를 쿠키 값으로 응답
(5) Browser : 쿠키 값 저장

[ 권한이 필요한 요청을 할 경우 ] Ex) 게시글 수정

(1) Browser → App Server : 쿠키 값(Session ID) 전송
(2) App Server → DB : Session ID 조회
(3) DB → App Server : 조회 결과 해당 회원이 있다면, 요청 허용
(4) App Server → Browser : 요청 수행

2. 토큰기반 인증

: 인증 정보를 서버가 아닌 클라이언트가 직접 들고 있는 방식

[ 장점 ]

  • 클라이언트에 토큰이 저장되기에 서버 메모리 부담 감소
  • 멀티 디바이스 환경 부담 감소
  • 세션기반에 비해 서버 부담 완화
    : JWT는 메시지를 담는 수단이기 때문에, 애플리케이션이 자주 필요한 정보를 Payload에 담음으로써 필요한 정보를 위해 서버에 접촉하는 횟수를 줄일 수 있다.

[ 단점 및 방안 ]

  • 암호화가 풀릴 가능성을 배제할 수 없다.
    → 토큰의 만료기간을 짧게 설정

  • payload 자체는 암호화되지 않고 base64로 인코딩된 데이터이므로, payload가 중간에 탈취되면 디코딩을 통해 데이터가 보여진다.
    → JWE를 통해 암호화하거나 payload에 중요한 정보를 넣지 않는다.

  • 토큰을 탈취당할 경우, 유효기간이 지나기 전까지 조치를 취할 수 없다.
    → HttpOnly cookie에 토큰 저장, JWT 블랙리스트 관리(단, session 방식과 다를바 없음), 만료기간 짧게 설정(refresh token 운영 → refresh token 탈취를 막기 위한 resfresh token rotation 필요 혹은 refreash token은 1번만 사용되기 설정)

[ 동작 원리 ]

(1) Browser → App Server : 로그인 요청
(2) App Server → DB : 회원 조회
(3) DB → App Server : 해당 회원 조회
(4) App Server : JWT 생성
  • 헤더 : 서명 알고리즘에 사용된 알고리즘 명시
  • 내용 : payload
  • 서명 : Secret Key와 Payload를 서명 알고리즘을 이용하여 서명 생성
    -> 헤더, payload, 서명을 base64로 인코딩

(5) App Server → Browser : 생성된 JWT 전송
(6) Broswer : 쿠키나 로컬 스토리지와 같은 브라우저 저장 시스템에 JWT 저장

  • JWT를 갖고 있다는 거 자체가 로그인 성공을 의미
  • base64로 디코딩하여 payload의 내용 확인 가능

[ 인증이 필요한 경우 ]

(1) Browser → App Server : 브라우저 저장 시스템에 저장된 JWT 전송
(2) App Server : 받은 JWT를 base64로 디코딩

  • 브라우저가 전달한 Header와 Payload를 자신이 갖고 있는 Secret Key를 이용하여 서명 생성
  • 해당 서명이 브라우저가 전달한 서명과 일치한지 확인
  • 일치하다면, JWT를 전송한 브라우저는 이전에 서버가 로그인해준 적이 있는 사용자임이 확신할 수 있게 된다.

(3) App Server → Browser : 인증된 사용자임으로, 브라우저가 원하는 요청 수행


JWT, Json Web Token

: Json 형태로 된 데이터를 네트워크를 통해 서로 다른 장치끼리 안전하게 전송하기 위해 설계되었다.

활용 예시

  • 인증 (Authentication)
  • 정보 공유 (Information Exchange)
  • 권한 부여 (Authorization)
  • 단일 로그인 (Single Sign-On)

구성

: .(dot)을 구분자로 헤더, 내용, 서명을 구분하여 JWT 토큰 1개를 이룬다.

  • 헤더, 내용, 서명은 base64로 인코딩되어 전송된다.

(1) HEADER
: 토큰을 어떻게 검증하는가에 대한 내용

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

(2) PAYLOAD
: 토큰에 담긴 사용자 정보 등의 데이터 저장, Claim으로 구성
Ex) 누가 누구에게 발급했는지, 유효기간 등

  • Claim : Payload를 이루는 각 부분

Q) 왜 Claim, 즉 주장이라고 명칭하는가?

  • Payload를 받는 쪽과 보낸 쪽 모두에서 이 Claim을 곧대로 믿으면 안되기 때문에 주장이라고 명칭한다.
  • 서명을 통해 이 내용이 믿을 수 있는 내용인지 즉, 내용의 무결성을 검증한다.
    {
		"sub": "123456",
		"name": "John Doe",
		"email": "John@example.com"
	}

(3) SIGNATURE
: 서명. 부호화 시킨 header와 payload를 가지고 발급해준 서버가 지정한 secret key로 암호화 시켜 토큰 변조를 어렵게 한다.

[ 서명의 원리 ]

(1) 송신자 : Secret Key와 payload(내용)을 HMAC(서명 생성기)에 넣어, signature(서명)을 생성한다.

(2) 송신자 : header에 HMAC, payload, 생성된 signature을 base64로 인코딩하여(JWT) 수신자에게 전송한다.

(3) 수신자 : 수신 받은 JWT를 base64로 디코딩하여 payload 확인 가능.
차후, 인증이 필요할 때 JWT를 송신자에게 전송한다.

(4) 송신자 : 수신자에게 받은 JWT를 base64로 디코딩하여, Header의 적힌 알고리즘에 소유 중인 secret key와 디코딩된 payload를 대입하여 서명 생성

  • 해당 서명이 디코딩된 서명과 동일한지 확인
    - 동일하다면, 해당 payload는 이전 송신자가 생성한 것이 분명하게 된다.

Access Token 과 Refresh Token

: JWT는 탈취될 경우 위험성이 높다. 그렇다고 유효 기간을 짧게 두자니 사용자 입장에서 불편하다.
→ 유효기간이 다른 JWT 토큰 2개(Access Token, Refresh Token)을 통해 관리한다.

Access Token

  • 유효기간이 짧은 JWT 토큰, 평소 API 통신에 사용된다.
  • Payload : 만료 기간 정보 포함
  • Signature : Header + Payload를 비밀키로 암호화한 값을 담는다.
    • 만약, 공격자가 Payload의 만료기간을 바꾸더라도 Signature는 바뀌지 않기 때문에, 복호화된 Payload와 변경되 Payload간 불일치로 접근 권한을 주지 않을 수 있다.

Refresh Token

  • 유효기간이 긴 JWT 토큰, Access Token이 만료되었을 때 갱신하는 용도로 사용된다.

[ 탈취 위험 ]

  • OAuth에서는 Refresh Token Rotation을 제시한다.
    클라이언트가 Access Token을 재요청할 대마다 Refresh Token도 새로 발급하는 것
    → Refresh Token은 더이상 만료 기간이 긴 토큰이 아니게 된다.

번외

Q-1) HTTP가 비연결성으로 설계된 이유

: 하나의 서버와 여러 클라이언트 환경에서 모든 연결이 동시에 유의미한 통신을 하고 있을 확률이 적다. (= 서버의 자원 낭비) 따라서, HTTP 초기 모델은 클라이언트가 서버에 요청을 하고 응답을 받으면 바로 TCP/IP 연결을 끊도록 하는 방식 즉, Connectionless하게 운영하도록 하였다.


Q-2) 연결 지향인 TCP 위에서 HTTP는 왜 비연결성일 수 있는가? : HTTP 3 이전까지 HTTP는 TCP 기반으로 동작했다. OSI 7계층에 따르면, HTTP는 7계층에서, TCP는 4계층에 속해 있다.
  • TCP의 연결 지향 : 클라이언트가 요청을 보내지 않더라도 계속 연결을 유지해야 한다.
    → 연결을 유지하는 서버의 자원이 계속 소모
  • HTTP의 비연결성 : 실제로 요청을 주고 받을 때만 유지하고, 응답을 주고 나면 연결을 끊는다.
    → 최소한의 자원으로 서버 유지 가능

쉬운 이해를 위해, HTTP는 모니터, TCP는 HDMI 케이블이라고 비유해보자.

  • 모니터 사용을 위해선 HDMI 케이블이 항상 연결되어 있어야 한다. (연결지향)
  • 모니터의 경우 상황에 따라 전원을 킬 수도 끌 수도 있다. (비연결성)

즉, TCP는 말 그대로 수단일 뿐, 이를 조작하는 것은 HTTP라고 보면 된다.


Q-3) Access token과 Refresh token은 어디에 저장해야하는가?
[ Frontend 측면]

Access Token은 JS private Instance로 저장.
즉, const accessToken = xyz와 같이 frontend의 variable에 저장한다. = in-memory 저장

  • 페이지를 새로 고침할 경우, access token은 사라지게 될 것이다. = refresh token이 필요한 이유
  • access token이 사라지거나 만료시, cookie에 저장된 refresh token을 /refresh-token endpoint로 보내주어 새로운 access token을 발급받는다.

Refresh Token cookie setting :
(1) httpOnly(XSS 예방) 플래그 설정 : JS가 쿠키를 읽는 것을 방지
(2) secure = true : HTTPS에서만 요청을 보낼 수 있다.
(3) sameSite=strict(CSRF 예방) : Authorization Server가 사용자의 프론트엔드와 같은 사이트에서만 사용 할 수 있다.

  • 만약 아닐 경우, Authorization server는 CORS header를 백엔드에서 설정하거나 refresh token 요청을 인증된 웹사이트에서만 완료시킬 수 있도록 세팅해야한다.

※ Local Storage는 XXS 공격에 취약하기에 적합하지 않다.
-> Javascript를 사용해 쉽게 접근할 수 있게 때문
정확한 방식이 아닐 수 있다...


Q-4) 세션 방식과 토큰 방식은 언제 쓰는 것이 적절할까?

(1) 사이즈

  • 세션의 경우 Cookie 헤더에 Session ID만 실어 보내면 되기에 트래픽을 적게 사용한다.
  • JWT는 사용자 인증 정보와 토큰의 발급시각, 만료시각 등 담겨있는 정보가 Session ID보다 비대하기에 네트워크 트래픽을 훨씬 많이 사용한다.

(2) 안전성과 보안성

  • 세션의 경우, 서버에서 관리하기에 보안 측면에서 더 유리하다. Session ID를 탈취당하더라도, 서버측에서 해당 세션을 무효 처리하면 된다.
    또한, 모든 데이터가 서버에 저장되기에 아무나 함부로 열람할 수 없음으로 저장 데이터의 제한이 없다.
  • JWT는 서버가 트래킹하지 않고 클라이언트가 모든 인증 정보를 가진다. 따라서, 해커가 탈취하게 되면 만료될 때까지 피해를 입을 수 밖에 없다.
    더욱이, payload는 별도로 암호화가 되어있지 않으므로 민감한 데이터를 실을 수 없다. 저장 데이터의 제한이 있다.

(3) 확장성 : 수평 확장을 근거로

  • 세션 기반일 경우, 별도의 작업을 하지 않을 경우 수평 확장 시, 세션 불일치 문제가 발생한다. 이를 해결하기 위해선 Sticky Session, Session Clustering 등의 작업이 필요하다.
  • 토큰 기반 인증 방식, 서버가 직접 인증 방식을 저장하지 않고, 클라이언트가 저장하는 방식이기 때문에 세션 불일치 문제에서 자유롭다.
    이런 특징으로 HTTP의 비상태성을 그대로 활용할 수 있어 높은 확장성을 갖는다.

(4) 서버 부담

  • 세션 기반은 모든 데이터를 서버가 관리하는 만큼 데이터 양이 많아질수록 부담이 커진다.
  • 토큰 기반은 클라이언트가 인증 데이터를 직접가지기 때문에, 세션 기반에 비해 부담이 적다.

후기

  • 파면 팔수록, 공부할 내용이 많네요..
  • 내용이 많아 적지 못한, Cookie와 Session에 관한 내용은 차후에 업로드하겠습니다.
  • 마찬가지로 양이 많았던 HTTP에 관한 내용도 차후에 업로드하겠습니다.
  • 프론트엔드에서 각 토큰들을 저장하는 방식은 코딩 애플 유튜브 댓글이랑 구글링으로 찾은 내용 통합해서 정리하였는데, 이 또한 정확하진 않습니다.

혹시 틀린 내용 있으면, 댓글 부탁드립니다!


참고 자료

Youtube [ 코딩 애플 : JWT 대충 쓰면 님들 코딩인생 끝남 ]

Youtube [ 생활코딩 : JWT ]

LocalStorage vs Cookies: JWT 토큰을 안전하게 저장하기 위해 알아야할 모든 것

TCP의 연결지향과 HTTP의 비연결성

profile
이 또한 지나가리라

0개의 댓글