JWT - JSON Web Token

토큰 기반 인증

토큰 기반 시스템은 stateless하므로 유저의 인증 정보를 서버나 세션에 담아두지 않는다.

  1. 유저가 아디이와 비밀번호로 로그인을 한다.

  2. 서버측에서 해당 계정정보를 검증한다.

  3. 계정정보가 정확하면, 서버측에서 유저에게 signed 토큰을 발급해준다.

    signed는 해당 토큰이 서버에서 정상적으로 발급된 토큰임을 증명하는 signature이다.

  4. 클라이언트 측에서 전달받은 토큰을 저장해두고, 서버에 요청할 때마다, 해당 토큰을 함께 서버에 전달한다.

  5. 서버는 토큰을 검증하고 요청에 응답한다.

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

장점

무상태(Stateless)이며 확장성(scalability)가 있다.

토큰은 클라이언트 사이드에 저장하기때문에 완전 stateless하며, 서버 확장하기에 적합한 환경을 제공한다.

보안성

클라이언트가 서버에 요청을 보낼 때 쿠키를 사용함으로 인해 발생하는 취약점이 사라진다.

확장성(Extensibility)

토큰을 사용해 다른 서비스에서도 권한을 공유할 수 있다. 예를 들어서 Fackbook, Naver, Google, Kakao 계정으로 로그인할 수 있다면, 토큰에 선택적인 권한만 부여해 발급할 수 있다.

여러 플랫폼 및 도메인

서비스의 규모가 커지면, 우리는 여러 디바이스를 호환시키고, 더 많은 종류의 서비스를 제공한다. 토큰을 사용한다면, 어떤 디바이스에서도, 도메인에서도 토큰만 유효하다면 요청이 정상적으로 처리된다.

JSON Web Token

JWT는 웹표준(RFC7519)으로서 JSON 객체를 사용해 가볍고 자가 수용적인 방식으로 정보를 안전성 있게 전달해준다.

  • 많은 프로그래밍 언어에서 지원(C, Java, Python, C++, R, JavaScript, Ruby...)

  • 자가 수용적(self-contained) : JWT는 필요한 모든 정보를 자체적으로 지니고 있다.

  • 쉽게 전달 가능 : 웹 서버의 경우 HTTP 헤더에 넣어 전달할 수 있고, URL의 파라미터로 전달할 수 있다.

주로 회원 인증이나, 안정성있게 정보 교류를 할 때 사용된다.

{
  "typ" : "JWT",
  "alg": "HS256"
}
  • typ : 토큰의 타입을 지정

  • alg : 해싱 알고리즘을 지정(HMAC SHA256 , RSA)

Payload

Payload 부분에는 토큰에 담을 정보가 들어있따. 여기에 담는 정보의 한 조각을 clame 이라 하며, 이는 name,value 쌍으로 이루어져있다.

{
  "sub": "1234567890",
  "name": "Dahye Jeong",
  "iat": 1516239022
}

클레임은 크게 세분류로 나뉜다.

Registered claim

서비스에서 필요한 정보들이 아닌, 토큰에 대한 정보들을 담기 위해 이미 이름이 정해진 클레임들이다. 등록된 클레임의 사용은 모두 선택적(optional)하다.

registered claim

설명

iss

토큰 발급자(issuer)

sub

토큰 제목(subject)

aud

토큰 대상자(audience)

exp

토큰만료시간(expriation)

nbf

Not Before로 토큰 활성 날짜와 비슷한 개념

iat

토큰이 발급된 시간(issued at)

jti

JWT의 고유 식별자로, 중복처리를 방지하기 위해 사용

Public Claim

공개 클레임들은 충돌이 방지된 이름을 갖고 있어야한다. 충돌 방지를 위해서는 클레임이름을 URI 형식으로 짓는다.

{
    "https://velopert.com/jwt_claims/is_admin": true
}

Private Claim

클라이언트 <-> 서버 간에 협의하에 사용되는 클레임들이다. 공개 클레임과 달리 이름이 중복되어 충돌 될 수 있으므로 주의해야한다.

"name": "Dahye Jeong",

Signature

서명은 Header의 인코딩된 값과 Payload의 인코딩된 값을 합친 후 주어진 비밀키로 Hash를 생성한다.

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  your-256-bit-secret
);

이렇게 만든 해쉬를 base64형태로 나타내면된다.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

다음과 같이 생성되는 것을 확인할 수 있다.

base64

// payload or header 생성하기
const encodedPayload = new Buffer(JSON.stringify(payload))
                            .toString('base64')
                            .replace('=', '');
const crypto = require('crypto');
const signature = crypto.createHmac('sha256', 'secret')
             .update(encodedHeader + '.' + encodedPayload)
             .digest('base64')
             .replace('=', '');

다음과 같이 base64를 이용해 생성할 수 있다.

참고

Last updated