Computer Science

JWT(Json Web Token)

Yuna_dev 2021. 6. 21. 12:33

인증과 인가 글에서 웹 서비스 인증 방법으로 4가지를 간단하게 소개했었는데, 그중 토큰 기반 인증은 최근 모던 웹서비스에서 가장 많이 사용되는 방식이다. 토큰을 기반으로 하는 인증 시스템을 사용하는 데에는 여러 이유가 있다.

 

서버는 무상태성(stateless)의 속성을 가지고 있는데, 이는 클라이언트에서 전달받은 상태를 저장하지 않는다는 것이다. 상태 정보를 저장하지 않기 때문에 서버에서는 클라이언트에서 보내는 요청만으로 작업을 수행하고 마친다. 이러한 서버의 무상태성으로 서비스가 커지고 사용 유저가 많아지더라도 그에 맞춰 서버도 확장할 수 있게 되었다. 

 

또한 토큰기반 인증 시스템은 로컬 영역에 유저 정보를 저장하지 않기 때문에 session/cookie 시스템보다 보안을 높일 수 있다. 더불어 모바일 어플리케이션 개발을 할 때는 cookie 인증 시스템은 쿠키 storage를 따로 만들어야 하기 때문에 적합하지 않다. 토큰 기반 인증은 이러한 번거로움을 해결할 수 있으며, OAuth 기술을 사용하여 인증 정보를 다른 어플리케이션에 전달할 수 있다.

 

토큰 등장 배경 

1. 서버 기반 인증

기존의 인증 시스템에서는 서버에서 유저들의 정보를 기억하고 있어야 했다. 그래서 메모리, 디스크, 데이터 베이스 등에 유저들의 정보를 저장해왔고 이는 지금도 많이 사용되고 있다. 그러나 웹과 모바일의 사용자가 늘어날수록 서버에 저장하는 데이터 양이 많아지고, 인터넷 통신 속도가 빨라지면서 서버 과부화와 성능저하를 야기했다. 또한 모든 데이터를 매번 서버에서 받아오는 것이 속도적으로 느리다는 문제들이 발생했다. 

세션 인증기반 시스템을 사용하려면 많은 트래픽을 감당하기 위해 여러개의 프로세스를 돌리고, 서버를 추가해야하는데 이는 복잡한 과정이고 불가능하지는 않지만 서버를 확장하기가 어렵다.

웹 어플리케이션에서 세션을 관리할 때 사용되는 쿠키는 단일 도메인과 서브 도메인에서만 작동하도록 설계되어 있기 때문에 관리하는데 번거로움이 있다. 

 

2. 토큰 기반 인증

토큰 기반 인증 시스템은 서버처럼 무상태성(stateless)의 속성을 가진다. 이 시스템에서는 유저의 인증 정보를 서버나 세션에 담아두지 않는다. 서버에 데이터를 저장하지 않음으로써 서버 기반 인증의 많은 문제들이 해결된다. 

이렇게 서버 기반 인증 시스템의 단점을 보완하게 되면서 많은 모던 웹사이트와 어플리케이션에서는 토큰 인증방식을 사용하고 있다. 

 

JSON  Web  Token 

JWT 는 유저 정보를 JSON 객체로 안전하게 전송하기 위한 표준 방법으로, 현대 웹서비스에서 사용자들의 인증 작업을 처리하는 가장 좋은 방법이다. 이러한 토큰 기반 인증은 쿠키나 세션을 이용한 인증보다 더 보안성이 강하고 효율적인 인증 방법이다.

Json 포맷을 이용하여 사용자에 대한 속성을 저장하는 web token으로, self-contained 방식으로 필요한 모든 정보를 자체적으로 지니고 있다. 사용자 인증에 필요한 모든 정보를 토큰 자체적으로 담고있기 때문에, 서버에서 정보를 저장하고 유지할 필요가 없다. 토큰이 검증됐다는 것을 증명하는 signature도 포함하고 있어서 정보를 안전하게 전달한다.

 

JWT의 사용

Authorization (회원 인증)

  • 유저의 세션을 유지할 필요없이 유저의 요청시에 토큰만 확인하면 되므로, 세션관리가 필요없어서 서버자원을 아낄수 있다. JWT를 가장 일반적으로 사용하는 사용처다. 사용자가 로그인하면 이후의 각 요청에는 JWT가 포함되어 사용자가 해당 토큰으로 허용되는 경로, 서비스 및 리소스에 엑세스 할 수 있다.

정보 교환

  • JSON 웹 토큰은 당사자 간에 정보를 안전하게 전송하는 좋은 방법이다. public 또는 privit 키 쌍을 사용하여 JWT에 서명 할 수 있으므로 해당 조건에 맞는 사람인지 확인할 수 있다. 또한 서명은 헤더와 페이로드를 사용하여 계산되므로 콘텐츠가 변조되지 않았는지 확인할 수 있다.

 

JWT의 구조

 . 을 구분으로 하여 3부분으로 나눠져서 구성된다.

aaaaa.bbbbb.cccccc  // => 헤더. 내용. 서명

 

 

헤더(Header) : 헤더는 2가지의 정보를 담고 있다.

  1. type: 토큰의 타입을 지정한다. 토큰에는 많은 종류가 있고 서버는 다양한 종류의 토큰을 처리하기 위해 전송받은 typ에 따라 토큰을 다르게 처리한다.
  2. alg: 해싱 알고리즘을 지정하여 토큰을 검증할 때 사용한다. 이 알고리즘은 signature 부분에서 사용된다. 

 

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

 

 

정보(payload) : 토큰에 담을 정보가 들어있다. 

여기에 담는 정보의 한 '조각'을 클레임(claim)이라고 부르고, 이는 name/value한쌍으로 이뤄져있다.

여러개의 클레임을 토큰에 넣을 수 있다.

  • 등록된 클레임: 토큰에 대한 정보를 담기위해 이름이 정해진 클레임이다. 선택적으로 사용할 수 있다.
  • 공개 클레임: 충돌이 방지된 이름을 갖고 있어야 하며, 충돌 방지를 위해 URI 형식으로 이름을 짓는다.
  • 비공개 클레임: 클라이언트와 서버가 소통하기 위해 사용되는 클레임 이름이다.
{
    "iss": "velopert.com",   (등록된 클레임)
    "exp": "1485270000000",  (등록된 클레임)
    "https://velopert.com/jwt_claims/is_admin": true,  (공개 클레임)
    "userId": "11028373727102",  (비공개 클레임)
    "username": "velopert",  (비공개 클레임)
}

 

서명(signature) 

서명은 헤더의 인코딩값과 정보의 인코딩값을 합친 후 주어진 비밀키로 해쉬를 하여 생성한다. 이 서명 필드는 헤더와 페이로드를 통해 만들어지기 때문에 데이터 변조 후 재전송하는 것을 막을 수 있다. 서명 부분을 만드는 코드는 다음과 같다.

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

 

JWT의 장점과 단점

장점

1. 무상태성(stateless)이며 확장성이 있다. 

- 토큰은 클라이언트 사이드에 저장하기 때문에 서버를 무상태로 만들 수 있고, 서버를 확장하기가 쉽다. 별도의 저장소가 필요하지 않아서 서버자원을 절약할 수 있고, 인증과정에서 다른 곳을 거치지 않을 수 있어 효율적이다. 인증 정보를 가진 특정 서버에만 트래픽이 몰릴 일도 없기 때문에 서버 부하를 줄일 수 있다. 

 

2. 보안을 강화할 수 있다. 

- session/cookie를 사용하지 않음으로 보안을 더 강화할 수 있다. 토큰을 구성하는 헤더와 페이로드를 가지고 서명 필드(signature)를 생성하므로 데이터 변조 후 재전송을 막을수 있다.

 

3. 인증 정보를 다른 웹서비스에 전송할 수 있다.(OAuth)

- 토큰을 사용하여 다른 서비스에서 유저 정보 접근 권한을 공유할 수 있다. 특히 토큰에 선택적인 권한만 부여하여 발급할 수 있다. 

 

4. 여러 플랫폼과 도메인에 호환될 수 있다. 

- 서버기반 인증 시스템에서는 CORS 문제가 흔히 발생하는데, 이는 어플리케이션의 규모가 커질수록 해결하는데 번거로움이 있다. 그러나 토큰을 사용한다고 가정했을때, 토큰만 유효하다면 요청들이 정상적으로 처리된다. 서버측에서 어플리케이션의 응답부분에 다음과 같은 헤더만 포함시켜주면 된다.

Access-Control-Allow-Origin: *

 

단점

  • 토큰을 탈취당할 경우, 대처하기가 어렵다.(유효기간을 기다리거나 token refresh를 해야한다.)
  • JWT의 경우, 토큰의 길이가 길기 때문에 요청이 많아질수록 서버 자원의 낭비가 많아진다.
  • 로그인시 발급되는 이 토큰은 자격 증명이므로 보안에 주의를 기울여야 한다. 보안 부족으로 인해 민감한 세션 데이터를 브라우저 저장소에 저장해서는 안된다.

 

JWT의 작동 방식

JWT는 토큰 기반의 인증 시스템은 사용자들에게 토큰을 발급하고, 서버에 요청을 할 때 헤더에 토큰을 함께 보내도록 하여 유효성 검사를 하는 방식의 시스템이다. 구현 방식은 다음과 같다.

  1. 사용자가 아이디와 비밀번호로 로그인을 한다.
  2. 서버 측에서 해당 사용자의 계정 정보를 검증한다.
  3. 계정 정보가 정확하다면, 서버측에서 사용자에게 서명된 토큰을 발급한다.
  4. 클라이언트 측에서 전달받은 토큰을 저장해두고, 서버에 요청을 할 때마다 HTTP 헤더의 Authorization 필드에 해당 토큰을 담아 서버에 함께 전달한다.
  5. 서버는 유효한 토큰인지 검증하고, 적합하면 요청에 응답한다.