본문 바로가기

Web 개발

로그인 구현 관련 학습 (쿠키/세션/jwt/Oauth)

로그인 기능 구현에 필요한 사전 지식

인증(Authentication)

  • 클라이언트의 신원을 확인하는 것
  • 로그인하지 않은 클라이언트라면 ID/PW를 입력 받아 DB에 저장된 User 정보와 일치하는지 확인
  • 로그인한 클라이언트라면 로그인시 발급한 정보(상태 유지 기술)를 통해 신원을 확인한다.

인가(Authorization)

  • 리소스에 접근하는 접근자의 권한을 확인하고 허가 여부를 결정
  • 신원 확인(인증)이 이루어진 클라이언트를 대상으로 행해진다.

상태 유지 기술

  • 로그인 이후 클라이언트가 서버와 지속적인 요청/응답을 주고 받는 경우에, stateless 하고 connectionless한 http프로토콜로는 서버가 클라이언트가 로그인한 유저가 맞는지 확인할 수 없다.
  • 즉, 지속적인 인증이 힘들다. 이를 위해 서버는 로그인 성공시 클라이언트에게 자신을 입증할 신분증을 제공하여 이후 인증 절차를 ID/PW 요구 없이 수행할 수 있도록 한다.
  • 신분증 발급 기능을 위한 기술이 상태 유지 기술이며 쿠키, 세션, jwt가 있다.

쿠키

  • 클라인언트의 브라우저에 저장되는 key-value형식의 데이터
  • 쿠키는 유효기간을 지정할 수 있으면 만료시 삭제된다.
  • 서버에서 http 응답시 Set-Cookie 헤더 옵션을 통해서 클라이언트에 쿠키를 전달할 수 있다.
  • 쿠키는 브라우저에 저장되면 클라이언트의 매요청마다 요청 헤더에 쿠키를 담아 요청한다.
  • 활용 예시) 로그인, 쇼핑몰 장바구니, 팝업 "더 이상 이 창을 보지 않음" ...

세션

  • 세션은 쿠키를 기반으로 동작한다.
  • 세션은 쿠키(클라이언트 브라우저)에 데이터를 저장하는 대신 서버에 특정 클라이언트의 데이터를 전달한다.
  • 해당 클라이언트의 데이터가 저장된 세션의 ID 값을 쿠키로 보내고 클라이언트는 이후 요청에 쿠키에 저장된 세션 ID를 서버에 제공한다. 서버는 세션 ID를 통해 클라이언트를 확인 할 수 있다.
  • 세션은 클라이언트 정보가 서버에 저장되므로 쿠키보다는 보안상 안전하다.
  • 세션은 클라이언트 정보를 확인하기 위해 자주 참조 되므로 서버의 DB가 아닌 메모리상에 저장한다.
  • 동시 접속자가 많은 경우 서버에 부하가 발생한다.
  • 서버의 메모리에 저장되니 scale-out 환경의 경우 세션 데이터를 모든 서버가 동일하게 갖고 있기 힘든 데이터 정합성을 고려해야하는 문제가 있다.

jwt

  • jwt의 동작 방식은 세션보다는 쿠키에 가깝다. 왜냐하면 클라이언트의 정보를 클라이언트측에 저장하기 때문이다.
  • 브라우저의 쿠키에 저장하는 대신 jwt라는 토큰에 클라이언트 정보를 담아서 암호화한 후 클라이언트에게 제공한다. 클라이언트는 이를 스크립트 변수(메모리), 로컬 스토리지, 쿠키 등에 저장하여 이후 요청에 jwt 실어 보내서 인증을 진행한다.
  • 서버에서 관리하는 비밀키를 이용하여 암호화함으로 jwt내에 저장된 민감한 클라이언트 정보를 확인할 수 없다.
  • jwt는 크게 3개의 영역 header, payload, signature으로 나뉜다.
  • header는 토큰의 타입과 암호화 알고리즘으로 구성된다.
  • payload는 클레임을 담는 영역이다. 클레인은 정보의 조각을 의미한다. 클레임에는 등록된 클레임, 공개 클레임, 비공개 클레임 세 종류가 있다. 등록된 클레임을 통해 토큰 만료시간을 설정할 수 있다.
  • signature영역은 jwt의 무결성을 보장한다. header와 payload의 base64 인코딩 값을 합쳐서 서버에서 관리하는 비밀키로 해싱한 값이 signature 영역이다. 해시값을 통해 토큰의 유효성 검증시에 토큰이 변조되었는지 확인 할 수 있다.

로그인 플로우

쿠키/세션/jwt 어떤 기술을 사용하는가에 따라 로그인 플로우는 달라진다.
하지만 기본적으로 아래와 같은 흐름으로 동작한다.

  1. 클라이언트에서 서버로 ID/PW 제출 - 로그인 시도
  2. DB에 저장된 ID/PW 정보와 클라이언트로 부터 전달 받은 ID/PW 일치 여부 확인(최초인증)
  3. 인증 성공시 쿠키/세션/jwt 방식에 맞게 인증 정보 발행(ex. userID를 담은 쿠키, 세션 ID를 담은 쿠키, userID를 담은 jwt)
  4. 인증 정보를 클라이언트에게 전달
  5. 클라이언트의 리소스 요청(인증 정보와 함께)
  6. 클라이언트에서 보낸 인증정보를 확인하여 클라이언트 인증
  7. 인증된 클라이언트의 권한을 확인하여 리소스 접근에 대한 인가 진행
  8. 인가된 사용자라면 리소스 응답

위의 로그인/인증/인가 기능 외적으로 인증 정보타입(쿠키/세션/jwt)에따라 이를 관리해주는 로직이 필요할 수 있다.

로그인 구현에 jwt를 사용하고자하는 이유

  • 쿠키는 스크립트에서 쉽게 접근할 수 있다. 또한 쿠키안에있는 정보를 쉽게 확인 할 수 있고 이를 위변조하여 사용하여도 서버측에서 이를 확인 할 수 없다.
  • 또한 쿠키가 공격자에게 탈취되면 이를 이용해 공격자가 인증을 수행할 수 있게된다.
  • 이러한 이유로 로그인 구현에 있어 쿠키보다는 조금더 안전한 세션을 사용하는 것이 나아보임.
  • 왜냐하면, 세션은 인증에 사용되는 민감한 정보들을 서버에 저장을 하고 세션 ID만을 클라이언트가 관리하게하여 보안상 안전하게 사용할 수 있기 때문이다.
  • 하지만, 세션을 이용한다면 서버 성능에 부하가 발생한다. 세션을 저장하기위해 서버의 메모리가 소비되고 인증 절차는 로그인한 대상의 거의 모든 요청에서 이루어지기에 매우 빈번하게 조회된다.
  • 세션은 빈번하게 조회되기에 DB단에 저장되면 오버헤드가 커서 서버의 메모리단에서 저장된다. 그렇기 때문에 scale-out 환경에서 서버간에 세션 데이터 정합성 문제가 발생한다.
  • 이러한 문제들로 인해 로그인 구현에 쿠키/세션 대신 jwt가 사용된다.
  • jwt는 인증 정보를 jwt의 payload에 저장하며 토큰 발급시에 비밀키를 이용하여 암호화를 진행한다. 또한 signautre로 토큰의 위변조를 탐지한다.
  • 또한 클라이언트사이드에서 관리하기에 서버의 부하와 세션데이터 정합성 문제에서 자유롭다.
  • jwt도 제대로 사용하려면 refresh 토큰을 서버사이드에 저장하여 관리한다. 하지만, refresh토큰의 참조 빈도가 세션에비해 훨씬 낮기에 DB 단에서 저장 관리 할 수 있고 때문에 데이터 정합성 문제에서 자유로울 수 있다.

jwt 로그인 구현시 고려할 점

  • refresh rotate를 할 것 (보안)
  • access / refresh 토큰의 클라이언트단의 저장 위치
    • access는 스크립트 변수로
    • refresh는 http only 쿠키로 관리하는 것이 좋아 보임
  • refresh 토큰을 통한 access 토큰 reissue 로직
  • refresh 토큰의 서버에서 어떻게 관리할 것인지?
  • 토큰의 기간은 어떻게 설정하는 것이 합리적일까