JWT 방식의 인증 정리

jiho·2021년 11월 4일
0

express

목록 보기
1/1

JWT 완벽하게 이해하기

우선 알고 있는 세션기반의 인증부터 소개하면 JWT의 필요성에 접근해보겠습니다.

이 글의 정리하는 과정에서도 JWT를 정확히 이해하고 있지않습니다.

글을 작성하면서 공부를 겸해서하니 정확하지않는 정보가 있을 가능성이 있습니다.

Session 기반의 인증은 간단합니다. 흔히 세션키를 Cookie에 두고 소통해서 서버 메모리측에 사용자 정보를 유지하는 방법입니다. 해당 방식은 서버 메모리를 많이 소비한다는 측면에서 단점이 부각됩니다. 서버의 메모리 자원은 비용과 직결됩니다.

그리고 정말 중요한 내용이 더 있습니다.

그리고 확장성 또한 걸림돌이 됩니다. 사용자의 수가 증가할 때 아마 많은 서버 인스턴스가 필요할 것입니다. 그에 따라 많은 서버 간의 세션 정보들을 공유를 위해서 redis와 같이 인메모리형태의 세션 저장소를 여러 서버 인스턴스가 공유해야할 것입니다. 만약 이러한 설계를 거치지않는다면 하나 접속 세션은 하나의 인스턴스에 국한될 수 밖에 없고 Round Robin방식을 따르는 Load Balancer의 장점을 취할 수 없을 것 입니다.

JWT는 세션기반 인증의 서버 메모리 절약 + 확장성 문제를 해결해줄 수 있습니다. 물론 무조건적으로 토큰 기반의 인증이 옳다는 것은 아닙니다. 보안 관점에서는 토큰에 비해 세션이 우세하다고 주장하는 개발자들도 있습니다. 그 부분도 JWT를 정리하면서 접근해보겠습니다.

그리고 쿠키 세션 로그인은 쿠키를 허용안하면 쓰지 못할 수 있다. 아래는 제가 구독해서 사용하는 Packt라는 기술 서적 출판사의 사이트입니다. 항상 아래와 같이 쿠키를 허용할지 물어보는 창이 뜨게 됩니다. 쿠키 허용여부를 보여줘야하는 정책이 있는듯 합니다.

![스크린샷 2021-11-04 오후 9.06.53](/Users/sonjiho/Library/Application Support/typora-user-images/스크린샷 2021-11-04 오후 9.06.53.png)

JWT란?

https://www.akana.com/blog/what-is-jwt

몇 번 공부를 시도했었지만 현재는 또 까먹은 상태입니다. 위 글을 해석하며 정리해보는 시간을 가져보겠습니다.

JWT는 API 보안 측면에서 인기가 점점 높아져가고 있습니다. 우아한 테크 캠프 기간중에 개발자분들 모두 JWT를 선호하는 듯했습니다.

JWT 또는 Json Web Token 는 서버와 클라이언트 사이에 공개되서는 안되는 정보의 교환을 하기 위해 사용되는 open standard 입니다. 각 JWT는 몇개의 claim(토큰에 담긴 정보)을 포함해서 인코딩된 JSON 객체를 포함합니다. 토큰이 발행된 후 claim들이 수정될 수 없는 것을 확시히하기 위해서 JWT는 암호화 알고리즘을 사용해서 인증을 받습니다.

정의를 굳이하자면 위 내용으로 설명할 수 있을 것 같습니다. 여기서 claim은 영문 해석으로 주장,권리 등등이 될 수 있지만 프로그래밍적인 용어로 해석하기에는 무리가 있어서 우선 "토큰에 담긴 정보" 정도로 이해하고 넘어가면 좋을 듯합니다.

Token은 무엇일까?

잠시 JWT를 바로 알아보기전에 Token 자체에 대해서 이해해보겠습니다. 토큰은 identity 같은 것을 나타내는 문자열 데이터를 의미합니다. 인증의 경우, non-JWT 기반의 토큰은 정보를 받는 사람이 보내는 사람의 identiy를 검증할 수 있도록해줍니다. JWT와의 핵심적인 차이는 문자열 그자체에 담긴 의미의 부재라고 볼 수 있습니다. (JWT는 토큰 내용 자체에도 의미가 있습니다.)

JWT는 어떻게 동작할가?

JWT는 몇 가지 claim들을 포함하고 있다는 점에서 다른 웹 토큰들과 다릅니다. Claim는 정보를 전송하기 위해 사용됩니다. claim들이 무엇이냐는 사용사례에 따라 의존적입니다. 예를들어 cliam은 이슈를 발행한 사람이 누군지 확인하기 위해사용될 수 있고 얼마나 오랫동안 유효한지, 또는 클라이언트에서 허가된 권한들이 무엇인지와 같은 메타 데이터가 될 수 있습니다.

JWT는 dots(.)에 의해 나눠진 3가지 영역으로 구성된 스트링입니다. base64를 사용해서 serialized 됩니다. 가장 일반적인 serialization 포맷인 compact serialization의 경우, JWT는 xxxx.yyyy.zzzz와 같이 보입니다.

일단 decoded되면, 두가지 JSON 문자를 얻을 것입니다.

  1. header 와 payload
  2. Signature

![스크린샷 2021-11-04 오후 8.50.28](/Users/sonjiho/Library/Application Support/typora-user-images/스크린샷 2021-11-04 오후 8.50.28.png)

헤더는 토큰의 타입과 signing 알고리즘 정보를 포함합니다. 이 경우에는 JWT가 토큰 타입이 됩니다.

payloads는 claims들을 포함합니다. JSON string으로 보여지고 이 정보들은 유저가 요청한 동작을 수행할 권한을 가지고 있는지 검증하기 위해서 특히 서버에 의해 사용됩니다.

JWT를 위한 필수적인 Claim은 없습니다. 하지만 JWT를 기반으로하는 다른 표준들에서는 필수적인 claim이 만들어질지도 모릅니다. 예를들어 OAuth2.0 에서 bearer access token으로 JWT를 사용할 때, iss, sub, aud 그리고 exp는 존재해야합니다.

(설명이 모호한 부분 - 구체적인 설명이 필요합니다.)

그리고 마지막으로 signature (서명)는 토큰이 변하지 않았음을 알려주는 역할을 합니다. JWT를 만드는 쪽에서 header와 payload를 secret(발행자와 받는 쪽 둘 다 아는 값)을 가지고 암호화합니다. 토큰이 사용될 때, 받는 쪽은 header 와 payload가 signature와 매칭하는지 검증하게 됩니다.

JWT 예시 : OAuth Bearer Tokens

JWT를 사용하는 일반적인 방식은 OAuth bearer token 입니다. 이 예시에서 인증 서버는 client가 요청했을 때 JWT 토큰들을 생성하고 다른 사람이 수정할 수 없게학 위해 그것은 서명합니다. 그리고 client는 JWT를 REST API를 요청할 때 함깨 보냅니다. REST API는 JWT가 올바르다는 것을 결정하기 위해 JWT의 서명이 해당 토큰의 payload와 header와 매칭되는지 검증합니다. REST API 가 JWT를 검증했다면, 사용자의 요청을 허가하거나 거부하기 위해 claim들을 사용할 수 있습니다.

간단히 말하면, 우리는 JWT bearer 토큰을 보안 빌딩에 들어가기 위한 identity 뱃지로 생각할 수 있습니다. 이 뱃지는 특별한 권할들(claims)을 가지고있고 그러한 점에서 볼때, 빌딩의 선택된 영역만 접근할 수 있도록합니다. 이 비유에서 authorization 서버는 접수 데스크 혹은 badge를 발행하는 사람으로 볼 수 있습니다. 그리고 뱃지가 가치가 있다는것을 검증하기위해 회사로고를 거기에 프린팅할 것입니다.(이것은 JWT signature를 비유한 것입니다.) 만약 badge 소유자가 제한된 구역에 접근을 시도한다면, 뱃지속 권한들이 그들이 접근할 수 있는지를 결정하게 됩니다. JWT 속 Claim은 권한들을 의미합니다.

(내용을 도식화해서 표현하기)

JWT 을 사용하는 이유

간단히 말해, JWT들은 유저를 인증하거나 정보를 안전하게 공유하는 방법으로 사용됩니다.

전형적으로 private key, 또는 secret은 JWT를 sign하기 위해 발행자에 의해 사용됩니다. 주로 JWT를 만드는 서버가 되겠죠. JWT를 받는 사람은 토큰이 발행자에 의해 발행된 후 변경되지 않았음을 확실히하기 위해 signature를 검증합니다. 권한이 없는 쪽에서 signing key를 추측하고 JWT 내의 claim들을 변경하려고 시도하는 것은 매우 어렵습니다.

모든 서명 Algorithm이 동일한 결과물을 만든지는 않습니다. 예를 들어 어떤 서명 알고리즘들은 JWT를 검증하는 쪽과 토큰 발행자 사이의 공유된 secret 값을 사용합니다. 또 다른 알고리즘들은 public, private key를 사용합니다. private key는 오직 발행자만 알고 있고, public key는 널리 알려질 수 있습니다. public key는 signature를 검증하는데 사용될 수 있지만 private key는 오직 signature를 만들기 위해 사용될 수 있습니다. 이방식(공개키-비밀키 방식)은 공유되는 secret을 사용하는 방식보다 더욱 안전합니다. 왜냐하면 비밀키는 오직 하나의 장소에만 있으면 되기 때문입니다.

이러한 이유들 때문에, 서버는 유저를 확인하기 위한 정보들을 가진 데이터베이스를 유지할 필요가 없습니다. JWT를 발행한 서버와 그것을 검증하는 서버는 반드시 같지 않아도 된다는 뜻이기도 합니다.

JWT 실제 생성 과정을 아래의 벨로펀트님 블로그 읽고 한번 직접 구현해보면 훨씬 이해하기 가 쉽습니다.

https://velopert.com/2389

Refresh Token이란?

왜 리프레시 토큰이 필요한가?

리프레시 토큰은, JWT로그인 방식에 사용되는 JWT의 보안상의 단점을 보완하기 위해 구현한다.

사용자가 로그인하여 리소스에 접근할 수 있는 토큰을 액세스 토큰이라 한다.

액세스 토큰을 어떻게하던지 탈취하면( 클라이언트 상에 노출되어있으므로 비교적 탈취가 쉽다고한다. - 당연히 토큰만으로 로그인 여부를 결정하니 그런듯 합니다.)

해커는 로그인 한 유저처럼 리소스에 접근할 수 있다.

그래서 로그인으로 발급한 액세스 토큰의 유효기간을 짧게하여 공격받을 면적을 줄이는것으로 보안성을 높인다. 즉 해커가 탈취한 토큰 값을 사용하기도 전에 재발급해버려서 제대로 사용하지 못하게합니다.

하지만 리프레시 토큰도 탈취되면 아무 의미가 없게됩니다. 그래서 리프레시 토큰을 어디에 둬야 좋을까에 대한 갑론을박이 많아지게됩니다.

리프레시 토큰 어디다 둬야할가?

여러가지 방식이 많이 있지만 나열해보면

  • 쿠키에 refresh 토큰을 둔다.
    • httpOnly, secure 을 둬서 자바스크립트의 쿠키 접근을 제한하고 https로만 쿠키가 전송되도록한다.
  • 서버사이드에 DB에 refresh 토큰을 두고 refresh 토큰을 접근할 수 있는 Index를 쿠키에 둬서 아예 refersh token의 노출을 막는다.

리프레시 토큰 어디다 둬야할까? 에 대한 관련 게시글
https://doogle.link/jwt-%ED%98%B9%EC%9D%80-oauth2-%EC%9D%98-refresh-%ED%86%A0%ED%81%B0%EC%9D%84-%EC%96%B4%EB%94%94%EB%8B%A4-%EC%A0%80%EC%9E%A5%ED%95%B4%EC%95%BC-%ED%95%A0%EA%B9%8C/

보안관련 쿠키 옵션
https://nsinc.tistory.com/121

Express & jsonwebtoken 실습

https://www.digitalocean.com/community/tutorials/nodejs-jwt-expressjs

profile
Scratch, Under the hood, Initial version analysis

0개의 댓글