코진남

쿠키 -> 세션 -> JWT 진화하는 인증체계 본문

CS

쿠키 -> 세션 -> JWT 진화하는 인증체계

woojin126 2022. 3. 26. 15:06

JWT를 프로젝트마다 사용하긴 했지만 재대로 된 개념을 잡지못하고 사용하는 것 같아 정리해보려고 합니다.

Cookie

  • 쿠키는 클라이언트(브라우저) 로컬에 저장되는 키와 값이 들어있는 작은 데이터 파일입니다.
  • 사용자 인증이 유효한 시간을 명시할 수 있으며, 유효 시간이 정해지면 브라우저가 종료되어도 인증이 유지된다는 특징이 있습니다.
  • 쿠키는 클라이언트의 상태 정보를 로컬에 저장했다가 참조합니다.
  • 클라이언트에 300개까지 쿠키저장 가능, 하나의 도메인당 20개의 값만 가질 수 있음, 하나의 쿠키값은 4KB까지 저장합니다.
  • Response Header에 Set-Cookie 속성을 사용하면 클라이언트에 쿠키를 만들 수 있습니다.
  • 쿠키는 사용자가 따로 요청하지 않아도 브라우저가 Request시에 Request Header를 넣어서 자동으로 서버에 전송합니다.

 

쿠키는 간단히 이야기 하자면 아래와 같이 로그인 했을 때 Set-Cookie의 형태로 반환을 받은 쿠키를 토대로 로그인이 필요한 요청을 할 때마다 받은 쿠키를 던져 요청을 하는 동작 구조를 가집니다.

 

쿠키의 구성 요소

  • 이름 : 각각의 쿠키를 구별하는 데 사용되는 이름
  • 값 : 쿠키의 이름과 관련된 값
  • 유효시간 : 쿠키의 유지시간
  • 도메인 : 쿠키를 전송할 도메인
  • 경로 : 쿠키를 전송할 요청 경로

쿠키의 동작 방식

  1. 클라이언트가 페이지를 요청
  2. 서버에서 쿠키를 생성
  3. HTTP 헤더에 쿠키를 포함 시켜 응답
  4. 브라우저가 종료되어도 쿠키 만료 기간이 있다면 클라이언트에서 보관하고 있음
  5. 같은 요청을 할 경우 HTTP 헤더에 쿠키를 함께 보냄
  6. 서버에서 쿠키를 읽어 이전 상태 정보를 변경 할 필요가 있을 때 쿠키를 업데이트 하여 변경된 쿠키를 HTTP 헤더에 포함시켜 응답

쿠키 단점

 1. 쿠키는 노출이 되었을 때 id,pw에 대한 민감 정보까지 모두 노출되어 보안에 좋지 않다. 2. 조작당해서 들어올 가능성이 있다. 3. 웹 브라우저마다 쿠키에 대한 지원 형태가 다르기 때문에 다른 브라우저간의 공유가 불가능하다.

쿠키의 사용 예

  • 방문 사이트에서 로그인 시, "아이디와 비밀번호를 저장하시겠습니까?"
  • 쇼핑몰의 장바구니 기능
  • 자동로그인, 팝업에서 "오늘 더 이상 이 창을 보지 않음" 체크, 쇼핑몰의 장바구니

 

Session

  • 세션은 쿠키를 기반하고 있지만, 사용자 정보 파일을 브라우저에 저장하는 쿠키와 달리 세션은 서버 측에서 관리합니다.
  • 서버에서는 클라이언트를 구분하기 위해 세션 ID를 부여하며 웹 브라우저가 서버에 접속해서 브라우저를 종료할 때까지 인증상태를 유지합니다.
  • 물론 접속 시간에 제한을 두어 일정 시간 응답이 없다면 정보가 유지되지 않게 설정이 가능 합니다.
  • 사용자에 대한 정보를 서버에 두기 때문에 쿠키보다 보안에 좋지만, 사용자가 많아질수록 서버 메모리를 많이 차지하게 됩니다.
  • 즉 동접자 수가 많은 웹 사이트인 경우 서버에 과부하를 주게 되므로 성능 저하의 요인이 됩니다.
  • 클라이언트가 Request를 보내면, 해당 서버의 엔진이 클라이언트에게 유일한 ID를 부여하는 데 이것이 세션 ID입니다.

세션의 동작 방식

  1. 클라이언트가 서버에 접속 시 세션 ID를 발급 받음
  2. 클라이언트는 세션 ID에 대해 쿠키를 사용해서 저장하고 가지고 있음
  3. 클라리언트는 서버에 요청할 때, 이 쿠키의 세션 ID를 같이 서버에 전달해서 요청
  4. 서버는 세션 ID를 전달 받아서 별다른 작업없이 세션 ID로 세션에 있는 클라언트 정보를 가져와서 사용
  5. 클라이언트 정보를 가지고 서버 요청을 처리하여 클라이언트에게 응답

세션의 특징 및 단점

  • 각 클라이언트에게 고유 ID를 부여
  • 세션 ID로 클라이언트를 구분해서 클라이언트의 요구에 맞는 서비스를 제공
  • 보안 면에서 쿠키보다 우수
  • 사용자가 많아질수록 서버 메모리를 많이 차지하게 됨

세션의 사용 예

  • 로그인 같이 보안상 중요한 작업을 수행할 때 사용

 

JWT 등장

JWT(Json Web Token) 은 위와 같은 과정에서 나온 하나의 인터넷 표준 인증 방식입니다.

말 그대로 인증 정보들을 Token(Claim)에 담아 암호화시켜서 사용하는 토큰 입니다.

사실 기본적인 인증을 진행하는 구조는 Cookie와 크게 다르지 않지만, 강조되는 점은 JWT는 서명 된 토큰이라는 점 입니다. 공개/개인 키를 쌍으로 사용하여 토큰에 서명할 경우 서명된 토큰은 비밀 키를 보유한 서버가 이서명된 토큰이 정상적인 토큰인지 인증할 수 있다는 점입니다.

토큰 기반 시스템의 장점

토큰 기반 시스템은 stateless 합니다. 무상태. 즉 상태유지를 하지 않는다는 것이죠. 이 시스템에서는 더 이상 유저의 인증 정보를 서버나 세션에 담아두지 않습니다. 이 개념 하나만으로도 위에서 서술한 서버에서 유저의 인증 정보를 서버측에 담아둠으로서 발생하는 많은 문제점들이 해소됩니다.

세션이 존재하지 않으니, 유저들이 로그인 되어있는지 안되어있는지 신경도 쓰지 않으면서 서버를 손쉽게 확장 할 수 있겠죠?

토큰 기반 시스템의 구현 방식은 시스템마다 크고작은 차이가 있겠지만, 대략적으로 보면 다음과 같습니다:

  1. 유저가 아이디와 비밀번호로 로그인을 합니다
  2. 서버측에서 해당 계정정보를 검증합니다.
  3. 계정정보가 정확하다면, 서버측에서 유저에게 signed 토큰을 발급해줍니다.
    여기서 signed 의 의미는 해당 토큰이 서버에서 정상적으로 발급된 토큰임을 증명하는 signature 를 지니고 있다는 것입니다
  4. 클라이언트 측에서 전달받은 토큰을 저장해두고, 서버에 요청을 할 때 마다, 해당 토큰을 함께 서버에 전달합니다.
  5. 서버는 토큰을 검증하고, 요청에 응답합니다.

웹서버에서 토큰을 서버에 전달 할 때에는, HTTP 요청의 헤더에 토큰값을 포함시켜서 전달합니다.

 

JWT 구조는 각 (.)으로 구분이 되어있습니다.

  • HEADER
  • PAYLOAD
  • SIGNATURE

header에는 보통 토큰 타입이나, 서명 생성에 어떤 알고리즘이 사용되었는지 저장합니다.

지금 같은 경우에는 현재 토큰의 타입이 JWT이고, 앞서 이야기했던 개인키로 HS512 알고리즘이 적용되었습니다.

그리고 이 JSON은 Base64Url로 인코딩을 합니다.

 

Payload에는 보통 Claim이라는 사용자에 대한, 혹은 토큰에 대한 property를 key-value의 형태로 저장합니다.

그리고 이 JSON은 Base64Url로 인코딩을 합니다.

Cliam이라는 말 그대로 토큰에서 사용할 정보의 조각입니다. 어떤 Claim값을 넣는지는 개발자의 마음이긴 하지만 일단은 배울때는 JWT 표준 스펙을 알아보도록 합시다.

중요한 것은 Payload 에 민감한 정보를 담지 않는다는 것입니다. 위에 header와 payload는 json이 디코딩 되어있을 뿐이지 특별한 암호화가 걸려있는 것이 아니기 때문에 누구나 jwt를 가지고 디코딩을 한다면 header나 payload에 담긴 값을 알 수 있기 때문입니다.

 

  • Signature는 토큰을 인코딩하거나 유효성 검증을 할 때 사용하는 고유한 암호화 코드이다.
  • Signature 생성 과정
    1. 헤더와 페이로드의 값을 각각 BASE64로 인코딩한다.
    2. ‘1번에서 인코딩한 값’과 ‘서버 측에 숨겨둔 비밀키’를 Header에서 정의한 알고리즘(alg)으로 해싱을 한다.
    3. 2번에서 해싱된 결과를 다시 BASE64로 인코딩한다.
    4. 3번의 결과가 Signature 이다.

.

여기서 비밀키(시크릿키)는 클라이언트측에서 요청을 보내올때 검증용으로 사용하기 때문에 매우 중요합니다.

서명 부분을 만드는 슈도코드(pseudocode)의 구조는 다음과 같습니다.

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

이렇게 만든 해쉬를, base64 형태로 나타내면 됩니다 (문자열을 인코딩 하는게 아닌 hex  base64 인코딩을 해야합니다)

 

한번, 이 포스트에서 사용된 예제 헤더와 정보를 해싱 해볼까요?

먼저, 헤더와 정보의 인코딩 값 사이에 . 을 넣어주고, 합칩니다

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ2ZWxvcGVydC5jb20iLCJleHAiOiIxNDg1MjcwMDAwMDAwIiwiaHR0cHM6Ly92ZWxvcGVydC5jb20vand0X2NsYWltcy9pc19hZG1pbiI6dHJ1ZSwidXNlcklkIjoiMTEwMjgzNzM3MjcxMDIiLCJ1c2VybmFtZSI6InZlbG9wZXJ0In0

 

'CS' 카테고리의 다른 글

프레임워크 VS 라이브러리  (0) 2022.04.04
Rest vs Rest API vs RestFul  (0) 2022.04.04
프로세스 vs 스레드  (0) 2022.03.30
RESTful API 란?  (0) 2022.02.14
두 종류의 소프트웨어  (0) 2022.02.03