[Server] 토큰 기반 인증과 JWT

Cover image

JWT

최근 업무에서 긴급 이슈가 있었는데, 그 자세한 내용을 설명할 수는 없으나 가장 큰 원인 중 하나는 토큰 이슈였습니다. 그에 따라 오늘 토큰 기반의 인증과 가장 유명한 JWT(Json Web Token)에 대해 정리해볼려고 합니다.


토큰(Token) 기반 인증

토큰 기반의 인증은 모던 웹서비스에서 많이 사용됩니다. 특히 API를 사용하는 웹서비스를 개발해야한다면 토큰을 사용하는 방법이 가장 좋습니다.

기존 서버 기반 인증

기존의 서버 기반의 인증은 다음과 같은 구조를 가지고 있었습니다.

서버기반인증

하지만 이러한 서버 기반의 인증은 다음과 같은 문제를 가집니다. (그러나 아직 사용하는 곳도 많습니다.)

  • 세션

    • 유저가 인증을 할 때, 서버는 이 기록을 서버에 저장합니다. (= 세션)
    • 로그인 중인 유저가 늘어날 수록 서버의 램이 과부화됩니다. (DB에 저장하면 DB 성능의 무리가 오게됩니다.)
  • 확장성

    • 세션을 사용하는 경우에는 더 많은 트래픽을 감당하기 위해 여러개의 프로세스를 돌리거나, 여러 서버 컴퓨터를 추가하는 것이 어려워집니다.
  • CORS(Cross-Origin Resource Sharing)

    • 일반적으로 쿠키는 단일 도메인 및 서브 도메인엣만 작동하도록 설계되어 있는데 이러한 쿠키를 여러 도메인에서 관리하는 것은 번거롭습니다.

토큰 기반 시스템의 작동 원리.

토큰 기반 시스템은 stateless(상태를 유지하지 않음) 합니다. 이 덕분에 위에서 발생한 문제들이 해결됩니다.

토큰 기반 시스템은 다음과 같이 작동합니다.

토큰기반인증

이를 순서로 나타내면 다음과 같습니다.

  1. 유저가 로그인을 합니다.
  2. 서버가 계정정보를 검증합니다.
  3. 계정 정보가 정확한 경우, 서버측에서 signed 토큰을 발급해줍니다.
  4. 클라이언트 측에서는 전달받은 토큰을 저장하고, 서버 요청마다 토큰을 함께 서버에 전달합니다.
  5. 서버는 토큰을 검증하고 요청에 응답합니다.

토큰 기반 서비스를 선택하는 이유.

일반적으로 토큰 기반의 인증 시스템은 다음의 장점을 가집니다.

  • Stateless 서버

    • Stateful 서버 : 클라이언트에게 요청받을때마다, 클라이언트의 상태를 계속 유지합니다. 세션에 로그인 정보 등을 저장하고 계속 활용합니다.
    • Stateless 서버 : 상태를 유지 하지 않는 서버. 상태정보를 저장하지 않고, 서버는 클라이언트측에서 들어오는 요청만으로 작업을 진행, 이 경우에는 클라이언트와 서버의 연결고리가 없으므로 서버의 확장성에서 장점을 가집니다.
  • 모바일 어플리케이션에 적합

    • 만약에 Android와 IOS에서 작동하는 어플리케이션을 개발할 때, 안전한 API를 만들기 위해서는 쿠키 등은 좋은 해결책이 될 수 없습니다. (쿠키 컨테이너를 사용해야하기 때문에...)
  • 인증정보를 다른 어플리케이션으로 전달

    • 대표적인 서비스로 OAuth 등이 있습니다. 구글이나 페이스북, 카카오톡, 네이버와 같은 소셜 계정을 통해 다른 웹서비스에서도 로그인 가능합니다.
  • 보안

    • 토큰 기반의 인증을 통해서 어플리케이션의 보안을 높일 수 있습니다.

토큰 기반 서비스의 장점.

따라서 토큰 기반의 서비스는 다음과 같은 장점을 가집니다.

  • 무상태이며 확장성을 가짐

    • 기존에 설명한 것처럼 많은 리소스 소모를 줄일 수 있습니다.
  • 보안성

    • 쿠키를 사용하지 않으므로 쿠키 취약점을 방지할 수 있습니다.
    • 다만 토큰도 취약점이 존재합니다.
  • 확장성(Extensibility)

    • 토큰을 통해 다른 서비스에 권한을 공유함으로서 사업이나 분야를 확장할 수 있습니다.
    • 토큰에 선택적인 권한만 부여하여 발급할 수도 있습니다.
  • 여러 플랫폼 및 도메인

    • 토큰만 유효하다면 여러 플랫폼과 도메인에서 사용할 수 있습니다.
    • 서버측 어플리케이션 응답부분에 다음 헤더만 포함하면 Access-Control-Allow-Origin: * 서버에서는 쉽게 정리할 수 있습니다.
  • 웹 표준 기반

    • JWT는 웹 표준에 등록되어 있기 때문에 여러 환경에서 지원이 됩니다.

JWT(Json Web Token)

그렇다면 메인 디쉬인 JWT(Json Web Token)에 대해 이야기를 해보겠습니다.

JWT(JSON Web Token) 은 웹표준(RFC 7519)으로 두 개체에서 JSON 객체를 사용하여 가볍고 자가수용적인 (self-contained) 방식으로 정보를 안정성 있게 전달합니다.

JSON은 다음과 같은 특징을 유지합니다.

  • 수많은 프로그래밍 언어에서 지원됩니다. (대부분의 주류 프로그래밍 언어에서 지원)
  • 자가 수용적 (self-contained)

    • JWT는 필요한 모든 정보를 가지고 있습니다.
    • 토큰, 토큰에 대한 기본정보, 전달할 정보, signature 등을 가지고 있습니다.
  • 쉽게 전달 될 수 있습니다.

    • JWT는 자가수용적으로 두 개체 사이에서 손쉽게 전달된다.
    • 웹 서버의 경우 HTTP의 헤더에 넣어서 전달할 수 있고, ULR의 파라미터로도 전달 가능합니다.

JWT의 정의

JWT는 . 을 구분자로 3가지의 문자열로 되어있습니다. 구조는 다음과 같이 이루어졌습니다.

jwt

JWT 토큰을 만들때는 JWT를 담당하는 라이브러리가 자동으로 인코딩 및 해싱 작업을 해줍니다.

헤더

Headertypalg 의 두가지 정보를 지니고 있습니다.

  • typ : 토큰의 타입을 지정 - JWT
  • alg : 해싱 알고리즘을 지정 - HMAC SHA256이나 RSA가 주로 사용, signature에서 사용
{
  "typ": "JWT",
  "alg": "HS256"
}

인코딩에 대한 코드 예시입니다.

const header = {
  typ: 'JWT',
  alg: 'HS256',
}

// encode to base64
const encodedPayload = new Buffer(JSON.stringify(payload))
  .toString('base64')
  .replace('=', '')

정보(payload)

payload 부분에는 토큰에 담을 정보가 있습니다. 여기에 담는 정보의 한 조각을 클레임(claim) 이라고 부르며, 이는 name/value의 한쌍으로 이뤄져 있습니다.

클레임은 크게 3가지(등록-registered, 공개-public, 비공개-private)로 분류됩니다.

1. 등록된 (registered)클레임

등록된 클레임들은 이름이 이미 정해진 클레임이며, 모두 선택적입니다.

  • iss : 토큰 발급자 (issuer)
  • sub : 토큰 제목 (subject)
  • aud : 토큰 대상자 (audience)
  • exp : 토큰의 만료시간 (expiration), NumericDate
  • nbf : 토큰 활성 날짜
  • iat : 토큰이 발급된 시간, 나이를 확인할 수 있습니다.
  • jti : JWT의 고유 식별자, 중복처리를 방지하기 위해서 사용합니다.
2. 공개 (public) 클레임

공개 클레임들은 충돌이 방지된 이름을 가지고 있습니다. 일반적으로 충돌을 막기 위해 클레임을 URI 형식으로 짓습니다.

3. 비공개 (private) 클레임

등록된 클레임도 아니고 공개된 클레임이 아닙니다. 일반적으로 서버 협의에 사용되는 클레임입니다.

예제 payload는 다음과 같습니다.

{
  "iss": "github.com",
  "https://Azderica.github.io/is_admin": true
  "username": "Azderica"
}

마찬가지로 위의 코드처럼 encode 할 수 있습니다.

서명(signature)

JSON Web Token의 마지막 부분은 서명(signature)입니다. 서명은 헤더의 인코딩 값과 정보의 인코딩 값을 합쳐서 주어진 비밀키로 해쉬를 하여 생성

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

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

이렇게 만든 해쉬를 base64 형태로 나타냅니다.

JWT는 언제 사용하지?

  • 회원 인증

    • JWT를 사용하는 가장 흔한 시나리오입니다.
    • 유저가 로그인 시, 서버는 유저의 정보에 기반한 토큰을 발급하여 유저에게 전달하고 이후 요청시 JWT를 포함하여 전달합니다.
    • 서버측에서 유저의 세션을 유지할 필요가 없어서 리소스를 아낄 수 있습니다.
  • 정보 교류

    • JWT는 두 개체 사이에서 안정성있게 정보를 교환하기에 좋은 방법
    • 정보가 sign이 되어있기 때문에 정보가 조작되지 않았는지를 검증할 수 있음.

마무리.

토큰 기반의 인증 시스템과 JWT에 대해 알아보았습니다.


출처