Spring/Spring Security

Json Web Token (JWT)

공대키메라 2022. 3. 3. 17:50

1. Cookie vs Session vs JWT

Http의 가장 큰 특징은 비연결성, 무상태성이다. 즉, 한번 요청과 응답을 통하면 똑같은 일을 하기 위해서라도 동일한 정보를 전송하고 받아야 한다는 말이다. 이를 보완하기 위해 나온 기술이 Cookie와 Session이다.

 

이 장에서는 아주 핵심만 간략하게 설명하겠다. 

Cookie

웹사이트 접속시 접속자의 개인장치에 다운로드 되고 브라우저에 저장되는 작은 텍스트 파일. key - value형태.

 

장점 : client측에서 데이터를 저장 => server부담 없음

단점 : client에서 마음데로 조작이 가능. 용량이 작음

 

Session 

일정시간동안 같은 사용자(정확하게 브라우저를 말한다)로 부터 들어오는일련의 요구를 하나의 상태로 보고 그 상태를 일정하게 유지시키는 기술


장점 : server측에 데이터를 저장 => JSESSIONID가 server에 저장되어 안전. 

단점 : server에 과부하가 생길 수 있음. 그러므로 필수적인 데이터만 저장해야 한다. 

 

쿠키는 너무 나약(?)하고... 그렇다고 Session만 쓰기에는 좀 무겁고... 아니 둘의 중간은 없을까...? 

하면서 나온것이 JWT다. 

 

참고 : https://medium.com/swlh/why-do-we-need-the-json-web-token-jwt-in-the-modern-web-8490a7284482

 

2. 그래서 JWT? 넌 뭐니?

JWT에 대한 내용은 아주 쉽게 찾을 수 있다. 하지만 필자는 jwt.io에서 이 내용을 찾아보고자 했다. 

 

궁금한 분은 밑의 사이트에서 읽기를 추천한다. 

 

출처 : https://jwt.io/introduction

 

What is JSON Web Token?
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

 

Json Web Token(JWT)는 JSON 객체 부분들 사이에 안전하게 정보를 전송하는 컴팩트하고 self-contained way한 오픈 스탠더드다. 즉 안전하게 정보를 전송하기 위한 하나의 방식인 것이다. 

 

3. 왜 써야 하니?


결국 필요에 의해서 나온 것이 JWT이다. Cookie와 Session에 대해 설명할 때 보았듯이 cookie에도 적당히 데이터를 넣고, 너무 무겁지도 않은 암호화된 데이터를 필요로 하는 목적에서  JWT가 탄생한 것이다. 

 

이를 이용해서 얻을 수 있는 이점이 무엇인지 찾아봤다. 

 

이점

 

Authorization

jwt를 쓰는 가장 흔한 방법. 유저가 로그인 하면, 각각 연속적인 요청이 유저가 route, service, 혹은 resources에 접근을 허가하는지에 대한 정보를 jwt를 포함합니다. single sign on 은 작은 오버헤드와 다른 도메인에서도 쉽게 사용할 수 있는 능력 때문에 JWT를 요즘 널리 쓰는 특징입니다. 

 

* 오버헤드(overhead)는 어떤 처리를 하기 위해 들어가는 간접적인 처리 시간 · 메모리 등을 말한다.

 

Information Exchange

Jwt는 안전하게 party들 사이에 정보를 전달하기위한 좋은 방법입니다. 공개/비공개키를 이용해 JWT는 sign될 수 있고, 당신은 송신자가 누구인지 확실히 할 수 있습니다. 추가적으로, 서명이 헤더와 페이로드를 이용하는데 계산할 때, 내용물이 함부로 변경되지 않음을 증명할 수 있습니다. 

 

단점

 

Payload 정보가 많아지면 네트워크 사용량 증가, 데이터 설계 고려 필요

토큰이 클라이언트에 저장 => 서버에서 토큰 조작 못함.

 

4. JWT의 구조

 

JWT는 3개로 나뉜다.(곤충인줄... 머리, 가슴, 배...)

 

  • Header
  • Payload
  • Signature

이것을 순서대로 합쳐서 한줄로 보내면 다음과 같은 형식이 된다. 

xxxxx.yyyyy.zzzzz

 

헤더(Header)

signing 알고리즘에 사용된 암호화 형식과 타입이 담겨있다. 

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

 

페이로드(payload)

 

claims를 포함한다. claims란 entity와 추가적인 데이터에 대한 상태다. claims에는 다시 3개의 종류가 있다. 

 

  • registered claims : 의무적이진 않지만 추천되는 미리 정의된 claims의 세트다. 
  • public cliams : jwt를 사용하면서  마음대로 정의될 수 있다. 하지만 collision을 피하기 위해 IANA Json Web Token Registry 혹은 collision resistant namespace인 uri로 정의되어야 한다. 
  • private claimns : registered claims 혹은 public cliams가 아닌 것들의 사용을 허가하기 위한, party들 사이에 정보를 공유하기 위해 생성된 custom claims들이다. 
 - example -
{
    "sub":"1234567890",
    "name":"John Doe",
    "admin":true
}

 

Signature

signature 부분을 만들려면 암호화된 header, 암호화된 payload, 시크릿, 헤더에서 특화된 알고리즘을 가져와야만 한다. 

 

예를 들어, JMAC SHA256 알고리즘을 사용하고 싶으면, signature는 다음과 같은 방식으로 만들어질 것이다. 

 

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

 

signature는 메시지가 그 과정에서 변하지 않았는지 증명하기 위해 사용된다. 비공개 키에서 signed된 토큰들의 경우에, jwt의 전송자가 누구인지 또한 알 수 있다. 

 

Putting all together - 모두를 한번에!

출력은 HTML과 HTTP 환경에서 쉽게 통과 가능한 .(dot)으로 구분된 세개의 Base64-URL 스트링이다. 

SAML같은 XML 기반과 비교될 때 더 컴팩트하다. 

 

우리가 위에서 본 구조는 decoded된 것이고, .으로 구성된 것을 잘라서 사용한 알고리즘으로 다시 만들어보면 위의 구조 형식으 나올 것이다. 

 

5. JWT는 어떻게 작동하나?

그러면 이 JWT가 어떻게 작동하는지 알아보자.

 

인증에서 유저가 성공적으로 로그인 하면, JWT가 반환될 것이다. 

토큰은 credentials이 있기 때문에, greate care가 보안 이슈를 해결하기 위해 필수적이다. 일반적으로, 요구되는 것 보다 토큰을 길게 가지고 있어서는 안된다. => 일정 시간이 지나면 파기! 

 

그리고 보안의 취약성 때문에 브라우저 저장소에 민감한 session data을 가지고 있어서는 안된다.

 

사용자가 보호된 route 혹은 resource에 접근할 때 마다 유저 agent는 jwt를 보내야한다. 전형적으로 bearer schema 를 사용한 인증 헤더에서 말이다. 헤더의 내용물은 다음과 같다.

 

 Authroization: Bearer <token>

 

이것은 특별한 경우에 무상태성 인증 메커니즘일 수 있다. 서버의 보호된 루트는 인증 헤더에서 valid한 jwt를 확인할 것이다.  만약 존재한다면, 유저는 보호된 리소스들에 접근할 수 있다. 만약 jwt가 필요한 데이터를 포함한다면, 특정 작동을 위해 database query를 만들 필요가 줄어들 것이다! 늘 그런건 아니지만!

 

=> 마지막에 중요한 말이 있다. 인증을 할 때 마다 db에 접근하는 것이 아니라, 어느정도 선에서 db에 접근하는 불필요한 작업이 필요없이 jwt로 구현이 가능하다는 말이다. 

 

토큰이 인증 헤더에 보내지면, CORS 이슈가 발생하지 않을 것이다.

 

=> 여기서 뼈아픈 경험이 있는데... jwt 는 단순 string기반의 token이기 때문에 데이터를 전송한다는 개념에서 CORS 문제가 생기지 않는다는 것 같다. 

 

 

The following diagram shows how a JWT is obtained and used to access APIs or resources:

https://jwt.io/introduction

  1. The application or client requests authorization to the authorization server. This is performed through one of the different authorization flows. For example, a typical OpenID Connect compliant web application will go through the /oauth/authorize endpoint using the authorization code flow.
  2. When the authorization is granted, the authorization server returns an access token to the application.
  3. The application uses the access token to access a protected resource (like an API).

 

이렇게 JWT에 대해서 알아보았다. 

 

모든 내용은 인터넷에서 쉽게 찾을 수 있다. 

 

다만 중요한 것은 이것을 정말 내것으로 만드는 것이다. 필자는 그러기 위해 노력을 했고, 이것을 바탕으로 spring security 에 적용할 예정이다.