티스토리 뷰

웹을 개발하는 개발자에게 유저 인증은 기본적이면서도 중요한 절차이다.

회원가입, 로그인, 로그아웃 등 유저를 기반으로 한 기능을 구현하는 단계에서 유저 인증에 관하여 궁금한 점이 생길 것이다. (아마도...?)

 

최근 여러 블로그들을 살펴보면 Rest Framework 라이브러리의 JWT를 이용해 쉽게 JWT 인증을 구현하는 것을 볼 수 있다. JWT를 사용하고 있지만 어떻게 토큰이 만들어지고, 왜 JWT가 유용한지 정리해보자한다.

 

 

https://www.qu3vipon.com/django-jwt

위 블로그 내용을 참고한 내용을 바탕으로 정리하였습니다. 


Session based authentication

WT를 알아보기 전 먼저 session based authentication에 대해 알아야한다. JWT를 사용하기 이전에는 주로 세션을 이용한 인증이 이루어졌다. 세션을 이용해 인증하는 방법이 어떻게 작동하는지 살펴보면

1. 클라이언트에서 사용자의 인증 정보를 서버(백엔드)로 전달한다.

2. 서버는 인증을 처리한 뒤 해당 User에 대한 Session을 생성한다.

3. 세션 정보는 서버에 저장되고, 클라이언트는 세션 ID를 받아 브라우저에 저장한다.

4. 클라이언트는 이후 이루어지는 요청에 세션 ID를 이용한다.

5. 서버는 전달받은 세션 ID를 이용하여, 서버에 저장 중인 세션 정보로 인증을 처리한다.

6. 만약 세션 ID가 만료되었을 경우에는 초기단계, 즉 1번 과정부터 다시 이루어진다.

 

 

여기서 중요한 점은 Session 정보를 서버에서 관리한다는 점이다. 클라이언트도 브라우저에 세션 ID를 저장하여 사용하지만 세션 ID 자체는 중요한 정보가 담겨있지 않은 일종의 임시 비밀번호이며 실제 세션의 정보를 관리하는 것은 전적으로 서버의 역할이다.

 

Django에서 기본적으로 제공하는 session 인증을 사용해보면 django_session 테이블이 생성되고 그 안에 session_key, session_data, expire_date와 같은 필드로 session 정보가 저장됩니다.

 

여기서 생각해봐야할 점은, 세션 인증은 요청 할때마다 인증을 위해 서버의 데이터베이스를 탐색해야 한다는 점이다. HTTP 캐시 등을 이용하여 탐색 과정을 최적화 할 수는 있지만 기본적으로 세션 정보는 어딘가에 저장되는 구조이다. 요청 하나를 처리할 때 마다 서버에 저장된 세션 정보를 참조한다는 것은 굉장히 비효율적인 과정이라고 할 수 있다. 또 세션 정보를 저장하는 데이터베이스가 분산되어 있을 경우에는 각각의 데이터베이스를 탐색해야 한다.

 

이러한 배경에서 탄생한 것이 바로 JWT이다.

 


JWT - Token based authentication

세션과 JWT의 가장 큰 차별점은 토큰 자체에 유저의 정보가 담겨있다는 점이다. 세션 인증은 모든 유저의 정보와 세션 정보를 서버에서 관리하지만, JWT 인증에서는 토큰에 유저 정보를 담는다. JWT 인증 방식을 살펴보자

1. 세션 인증과 동일하게 클라이언트에서 사용자의 인증 정보를 서버에 전달한다.

2. 서버는 인증 정보로 인증을 처리하고 JWT를 생성하여 클라이언트에 전달한다.

3. 클라이언트는 JWT를 브라우저에 저장한다.

4. 클라이언트는 이후 이루어지는 request(요청)에 JWT를 담아서 사용한다.

5. 서버는 요청에 담겨진 JWT 토큰을 검증하여 인증을 처리한다.

6. JWT가 만료되면 토큰을 refresh한다.

 

세션 인증과의 큰 차이점을 살펴보면

  • JWT 인증방식에서는 토큰을 서버에 별도로 저장하지 않는다
  • 세션 인증은 유저를 인증하기 위해 서버의 DB를 참조하지만 JWT 인증은 서버를 참조하지 않는다

 

JWT가 생성되는 원리

 

JWT는 .을 이용하여 크게 세 개의 영역으로 구분되고 각각의 영역은 고유의 역할이 있다.

xxxxx.yyyyy.zzzzz

먼저 xxxxx 부분은 Header의 영역이다. 쉽게 말해서 JWT의 메타 정보를 나타낸다. 토큰의 타입(typ)을 명시하고 어떤 알고리즘이(alg) 쓰였는지를 나타낸다. 해당 정보는 Base64Url로 인코딩 되어 JWT의 첫 번째 영역이 된다.

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

 

yyyyy 부분은 payload 영역이다. 이 부분에 토큰이 만료되는 시간, 유저의 정보와 같은 실질적인 데이터를 담는 영역이다. payload 역시 Base64Url로 인코딩 되어 JWT의 두 번째 영역이 된다.

{
  "token_type": "access",
  "exp": 1368549658,
  "jti": "1foo2jwt3id4",
  "user_id": 1
}

 

zzzzz 부분은 JWT의 핵심인 Signature 영역이다. JWT는 signing을 통해 토큰 안에 유저 정보를 담으면서도 이를 안전하게 처리한다. Payload 영역에 담긴 유저 정보는 별도의 암호화 처리가 되어 있지않다. 

 

Signature가 만들어지기 위해서는 인코딩 된 header, 인코딩 된 payload 그리고 secret이 필요하다.

Simple JWT에서는 장고 프로젝트마다 사용하는 secret_key를 기본적으로 사용한다.Signature는 인코딩 된 header, payload, secret을 합친 뒤 이를 header에서 지정한 알고리즘으로 해싱한다.

header, payload, secret 값 중 하나라도 일치하지 않으면 signature는 완전히 다른 값을 갖게 된다. 이렇게 생성된 JWT는 클라이언트에 전달 되었다가 이후 요청에서 HTTP Header에 담겨서 서버로 전달된다. 이때 HTTP Header에는 Authenticate 영역에 jwt "jwt 토큰 값"의 형태로 전달된다.

 

서버가 JWT를 검증하는 과정은 JWT가 생성될 때와 마찬가지로 헤더, 페이로드, 시크릿을 이용하여 signature를 해싱한 뒤 전달받은 JWT의 signature와 같은지 확인한다. 만약 payload가 변조 되었다면 클라이언트에 받은 값과 서버에서 해싱한 값은 다르므로 인증이 거부당한다.

 
 
댓글