CS

[CS] 토큰 기반 인증과 JWT

  • -
토큰 기반 인증과 JWT에 대한 개념을 작성한 글입니다.

 

토큰 기반 인증이란?

토큰 기반 인증은 사용자 인증 확인 방법 중 하나입니다. 명칭 그대로 토큰을 사용하여 사용자를 확인합니다.

 

토큰은 서버에서 클라이언트를 구분하기 위한 유일한 값입니다. 서버가 토큰을 생성해서 클라이언트에게 제공하면, 클라이언트는 이 토큰을 갖고 있다가 여러 요청을 이 토큰과 함께 신청합니다. 그럼 서버는 토큰을 보고 요청한 사용자가 유효한 사용자인지 검증합니다.

 

토큰을 전달받고 인증받는 과정

  1. 로그인 요청: 클라이언트가 아이디와 비밀번호를 서버에 전달하면서 인증을 요청합니다
  2. 토큰 생성 후 응답: 서버는 아이디와 비밀번호를 확인해 유효한 사용자인지 검증합니다. 유효한 사용자일 경우엔 토큰을 생성해서 응답합니다.
  3. 토큰 저장: 클라이언트는 서버로부터 받은 토큰을 저장합니다.
  4. 토큰 정보와 함께 요청: 이후 인증이 필요한 API를 사용할 때 토큰을 함께 보냅니다.
  5. 토큰 인증: 서버는 요청과 함께 보내진 토큰이 유효한지 검사합니다.
  6. 응답: 서버는 토큰이 유효한 경우 클라이언트가 처리한 요청한 내용을 처리합니다.

 

토큰 기반 인증의 특징

토큰 기반 인증은 무상태성, 확장성, 무결성이라는 세 가지 특징을 가집니다.

 

  1. 무상태성
    • 무상태성은 서버가 클라이언트의 정보를 저장하지 않아 자원을 소비하지 않는 것을 말합니다.
    • 토큰을 저장하고 요청에 사용하는 것(=상태관리)은 클라이언트입니다. 따라 서버는 클라이언트의 인증 정보를 저장하거나 유지하지 않아도 되기 때문에 완전한 무상태로 효율적인 검증을 할 수 있습니다.
    • HTTP 통신은 서버의 확장성을 위해 기본적으로 무상태성을 지향합니다. (서버를 추가하는 작업이 수월해지기 때문입니다.)
  2. 확장성
    • 무상태성에서 언급한 것처럼 서버를 확장할 때 상태 관리에 신경쓰지 않아도 되기 때문에 서버 확장에 용이합니다.
    • 추가로 로그인 방식을 확장하기에도 간편합니다. (OAuth 로그인 등...)
  3. 무결성
    • 무결성이란 토큰을 발급한 이후에는 토큰 정보를 변경하는 행위를 할 수 없는 것을 말합니다.
    • 토큰 방식은 Hash-based message authentication 기법이라고 불리는데, 만약 누군가 토큰을 한 글자라도 변경하면 서버에서는 유효하지 않은 토큰이라고 판단하는 겁니다.

 

JWT란

JWT는 JSON Web Token의 약자입니다. JWT는 JSON 객체를 표준규약에 따라 생성한 암호화된 토큰으로 복잡하고 읽을 수 없는 string 형태로 저장합니다.

 

JWT의 구성

JWT는 세 부분으로 구성됩니다: Header(헤더), Payload(페이로드), Signature(서명). 이러한 부분은 점(.)으로 구분되어 하나의 문자열로 합쳐져 토큰을 형성합니다.

 

1. Header(헤더): 토큰의 유형과 해시 알고리즘을 지정하는 정보를 담습니다.

다음의 예시에선 JWT 토큰, HS256해싱 알고리즘을 사용한다는 내용입니다.

{
    "typ": "JWT", // 토큰의 타입을 지정합니다
    "alg": "HS256" // 해싱 알고리즘을 지정합니다.
}

 

2. Payload(페이로드): 클레임(claim)이라고도 불리는 정보의 조각들을 포함합니다. 클레임은 키값의 한 쌍으로 이루어져 있고 클레임은 서로서로 다른 종류의 정보를 나타내며, 등록된 클레임, 공개 및 비공개 클레임으로 나뉩니다.

 

JWT에서 사용되는 등록된 클레임(Registered Claims)은 토큰에 대한 몇 가지 기본 정보를 제공합니다. 이러한 클레임은 JWT 표준에 정의되어 있으며, 일반적으로 사용됩니다. 주요 등록된 클레임은 다음과 같습니다:

  1. iss (Issuer): 토큰 발급자를 나타냅니다.
  2. sub (Subject): 토큰의 제목을 나타냅니다.
  3. aud (Audience): 토큰의 대상(수령자)를 나타냅니다.
  4. exp (Expiration Time): 토큰의 만료 시간을 나타냅니다. 이 시간 이후에는 토큰이 더 이상 유효하지 않습니다.
  5. nbf (Not Before): 토큰의 활성화 시간을 나타냅니다. 이 시간 이전에는 토큰이 유효하지 않습니다.
  6. iat (Issued At): 토큰이 발급된 시간을 나타냅니다.
  7. jti (JWT ID): JWT의 고유 식별자를 나타냅니다.

공개 클레임(Public claim)은 공개되어도 상관없는 클레임을 말합니다. 충돌을 방지하는 이름을 가져야 하며, 보통 클레임 이름을 URI로 짓습니다. 비공개 클레임(Private claim)은 공개되면 안 되는 클레임을 말합니다. 

 

{
  "iss": "example.com", // 등록된 클레임
  "sub": "user1234567890",
  "iat": 1618654195,
  "exp": 1618657795,
  "aud": "https://example.com",
  "jti": "1234567890"
  "https://hulrud.online/admin": true, // 공개 클레임
  "email": "hulrud@gmail.com" // 비공개 클레임
}

 

3. Signature(서명): 헤더와 페이로드를 조합하고 비밀 키를 사용하여 서명된 문자열입니다. 이 서명을 사용하여 토큰이 유효한지 확인할 수 있습니다. 서명은 헤더의 인코딩 값과 내용의 인코딩 값을 합친 후에 주어진 비밀키를 사용해 해시값을 생성합니다.

 

발급받은 JWT를 이용해 인증을 하려면 HTTP 요청 헤더중에 Authorization 키값에서 Bearer + JWT 토큰값을 넣어 보내야 합니다.

*예시

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Content-Type: application/json
Accept: application/json

 

 

토큰 유효기간과 리프레시 토큰

토큰 유효기간과 리프레시 토큰은 인증 시스템에서 중요한 역할을 합니다. 각각의 개념을 설명해 드리겠습니다.

  1. 토큰 유효기간(Token Expiration):
    • 토큰 유효기간은 JWT 등의 토큰이 유효한 시간을 나타냅니다.
    • 토큰이 발급된 후에는 이 유효기간 동안만 사용할 수 있으며, 유효기간이 지나면 토큰은 더 이상 유효하지 않습니다.
  2. 리프레시 토큰(Refresh Token):
    • 리프레시 토큰은 주로 액세스 토큰(Access Token)의 유효기간이 만료되었을 때, 새로운 액세스 토큰을 발급하기 위한 수단으로 사용됩니다.
    • 보통 액세스 토큰은 유효기간이 짧게 설정되어 있기 때문에, 유효기간이 만료되면 클라이언트는 리프레시 토큰을 사용하여 새로운 액세스 토큰을 얻습니다.
    • 리프레시 토큰은 일반적으로 액세스 토큰보다 긴 유효기간을 갖고 있습니다.

 

리프레시 토큰의 필요성

가령 토큰의 유효기간이 하루라면, 하루동안 그 토큰으로 모든 요청을 인증 받을 수 있습니다. 이러한 상황은 토큰을 쓰더라도 보안에 매우 취약해지겠죠. 하루동안 내가 아닌 다른 사람이 토큰을 사용해도 서버는 전혀 알 수 없는 것이니까요.

 

그렇다면 토큰의 유효기간이 10분이라고 생각해봅시다. 이 경우에는 사용자가 서비스를 사용하는 동안에 10분에 한 번씩 로그인을 해야할 수도 있습니다. 이러한 서비스가 있다면 아마 불편해서 사용하지 않겠죠.

 

이러한 불편한 점들을 해결하기 위해 필요한 것이 리프레시 토큰입니다. 리프레시 토큰은 액세스 토큰과 별개의 토큰입니다. 사용자를 인증하기 위한 용도가 아닌 액세스 토큰이 만료되었을 때 새로운 액세스 토큰을 발급하기 위해 사용합니다. 액세스 토큰의 유효기간을 짧게 설정하고 리프레스 토큰의 유효 기간을 길게 설정하면 공격자가 액세스 토큰을 탈취해도 몇 분 뒤에는 사용할 수 없는 토큰이 되므로 더 안전해집니다.

 

 

  1. 인증요청: 클라이언트가 서버에게 인증을 요청합니다
  2. 액세스 토큰 & 리프레시 토큰 응답: 서버는 클라이언트에서 전달한 정보를 바탕으로 인증 정보가 유효한지 확인한 뒤, 액세스 토큰과 리프레시 토큰을 만들어 클라이언트에게 전달합니다.
  3. 리프레시 토큰 저장: 서버에서 생성한 리프레시 토큰을 DB에도 저장합니다.
  4. 토큰 정보와 함께 요청: 인증을 필요로 하는 AP를 호출할 때 클라이언트에 저장된 액세스 토큰과 함께 API를 요청합니다.
  5. 토큰 유효성 검사 & 응답: 서버는 전달받은 액세스 토큰이 유효한 지 검사한 후 클라이언트에서 요청한 내용을 처리힙니다.
  6. 만료된 토큰 정보와 함께 토큰 발급 요청: 시간이 지나고 클라이언트에서 원하는 정보를 얻기 위해 서버에게 API 요청을 보냅니다. 
  7. 토큰 만료 응답: 만료된 토큰이면 유효하지 않기 때문에 토큰이 만료되었다는 에러를 전달합니다.
  8. 리프레시 토큰과 함께 토큰 발급 요청: 클라이언트는 이 응답을 받고 저장해둔 리프레시 토큰과 함께 새로운 액세스 토큰을 발급하는 요청을 전송합니다.
  9. 리프레시 토큰 조회 & 유효성 검사: 서버는 이 응답을 받고 저장해둔 리프레시 토큰이 유효한지, DB에서 리프레시 토큰을 조회한 후 저장해둔 리프레시 토큰과 같은지 확인합니다.
  10. 새로운 액세스 토큰 응답: 만약 유효한 리프레시 토큰이라면 새로운 액세스 토큰을 생성한 뒤 응답합니다. 이후 클라이언트는 4번과 같이 다시 API를 요청합니다.

 


부족한 글 읽어주셔서 감사합니다. 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.