본문 바로가기
Web/Web 이론

JWT(Json Web Token)에 대한 이론적 이해

by DUSTIN KANG 2023. 12. 6.

인증과 인가

인증에 대해서는 알겠지만 인가에 대해서는 잘 이해가 안될 것이다.

인증(Authentication)은 사용자가 이 서비스의 대한 권한을 부여받은 사용자라는 것을 인증하는 절차이고 인가(Authorization)은 이미 인증이 끝난 사용자가 해당 기능을 사용하기 위한 권한을 허가받는 절차라고 한다.

 

 

오늘은 지난 세션인증 방식에 이어 토큰 인증에 대해 중심적으로 다루고 대표적인 JWT(Json Web Token)에 대해서도 알아보려고 한다.

 


인증 방식

먼저, Client가 회원가입을 하게되면 해당 ID와 비밀번호가 서버 테이블에 저장되게 된다.

이때 테이블에 PW 그대로 저장되는게 아니라 한번 Salting + Hashing을 거쳐 저장되기 때문에 보안을 어느정도는 유지할 수 있다. 

이렇게 해시값을 이용해 로그인을 할 수 있다. 웹 서버의 HTTP 프로토콜은 특성 상 상태를 유지하기 어렵기 때문에 인증을 관리하는 방법이 필요하다. 이 방법을 두가지로 나눌 수 있다.

 

출처 : NAVER D2

세션 기반 인증(세션 ,쿠키)

지난 로그인 포스팅과 같은 방식이다.

우선 사용자가 로그인요청으로 유효성 검사가 끝나면, 서버에 세션이 생성된다. 해당 세션 내부에 사용자의 정보가 저장되고 쿠키에 세션 ID가 저장된다. 로그인이 되었으면 클라이언트가 요청을 보낼때마다 쿠키에 있는 세션 ID를 서버에 전송해  해당 세션 ID를 통해 식별하면서 로그인 상태를 유지할 수 있게 되는 것이다.

 

  • 장점 : 서버 단에 저장되기 때문에 토큰 기반에 비해 안전하다.
  • 단점 : 여러 이용자가 많이 접속하게되면 오버헤드가 심해질 수 있다. 캐시 시스템으로 메모리 관리가 필요하다.

세션과 쿠키에 대한 자세한 개념은 해당 포스팅을 참고하면 됩니다.

출처 : https://sherryhsu.medium.com/session-vs-token-based-authentication-11a6c5ac45e4

 

토큰 기반 인증(JWT)

토큰 기반 인증 방식도 어찌보면 세션 기반 인증 방식과 비슷하지만 토큰을 사용한다는 차이가 있다.

로그인을 하게되면, 사용자의 정보와 비밀 키를 통해 토큰을 발급하고 클라이언트에게 응답한다. 클라이언트는 쿠키나 스토리지에 토큰을 저장한다. 사용자는 토큰을 서버에 전송하여 유저 정보를 파악할 수 있게된다. 

 

  • 장점 : 클라이언트 단에 저장되기 때문에 오버헤드에 신경쓸 필요 없다.
  • 단점 : 보안에 취약한 클라이언트가 토큰을 저장하게 되면 개인정보에 대한 위험이 있다. 그리고 토큰은 그자체로도 삭제할 방법이 없기 때문에 유효기간이 끝날때까지 사용이 가능하다. (보안 위험)

출처 : https://sherryhsu.medium.com/session-vs-token-based-authentication-11a6c5ac45e4


JWT

JWT(Json Web Token)은 토큰 기반 인증 방식 중 대표적인 방식이다.

Claim 기반으로 이루어져 있는 Web Token 방식으로 토큰 자체 정보를 사용해 정보를 안전하게 전달할 수 있다.

 

Claim 기반 방식에서 Claim은 사용자에 대한 프로퍼티나 속성을 이야기 한다. 즉, 토큰 자체가 정보를 갖고 있다는 말인데 JWT는 이 Claim을 JSON을 이용해서 정의한다. 그럼 이러한 형태 그래도 HTTP Header에 넣어 전달하는 걸까? 그렇진 않다. 해당 JSON 문자열은 BASE64 인코딩을 통해 하나의 문자열로 변환한다. 이 정보는 JWT에서 `Payload` 부분에 담겨 있다.

{
	"id" : "who",
    "role" : ["admin", "user"],
    "company" : "apple",
}

 

클레임의 종류로는 총 3가지가 있다. 자세한 상세 소개는 아래 Payload 부분에서 진행할 것이다. 

  • 등록된 클레임(Registered Claim) : 토큰에 대한 정보를 표현하기 위해 이미 정해진 데이터들이다. 
  • 공개 클레임(Public Claim) : 공개용 정보를 담기 위해 사용되는 클레임이다.
  • 비공개 클레임(Private Claim) : 서버와 클라이언트 사이에서 임의로 지정한 정보를 저장하기 위해 사용되는 사용자 정의 클레임이다.

 

JWT의 구조

JWT는 Header, Payload, Signature로 이루어지며, 각 부분이 BASE64로 인코딩되어 표현된다. 그리고 각 부분 마다 점(`.`)으로 구분한다. 그럼 이 세가지를 하나씩 알아보도록 하자.

JWT

헤더(Header)

토큰의 헤더에는 `typ`과 `alg`로 구성된다. 헤더 부분에는 서명(Signature) 방식을 정의한 JSON 형태가 들어간다. `alg`은 알고리즘 방식을 지정하며 서명이나 토큰을 검증할 때 사용한다. 그리고 `typ`은 토큰의 타입을 지정한다.

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

 

페이로드(Payload)

여기는 이전에 말했었던 클레임(Claim) 정보가 들어간다. 즉, 로그인한 사용자의 상태 정보이다. 

Django를 통해 생성한 JWT

 

상위 클레임을 보면 들어보지 못한 낯선 키(key)값을 확인할 수 있을 것이다.

  • 등록된 클레임(Registered Claim)
    • `exp`(expiration) : 토큰 만료 시간을 의미하며 NumericDate 형식으로 되어 있다.
    • `iat`(issued at) : 토큰 발급 시간을 의미하며 토큰 발급 이후의 경과 시간을 의미한다.
    • `jti`(JWT 토큰 식별자) : 중복 방지를 위해 사용하는 토큰 식별자로 일회용 토큰(Access Token)등에 사용된다.
  •  비공개 클레임(Private Claim)
    • `token_type` : 클라이언트와 서버 사이에 주고 받을 수 있는 임의의 토큰 정보이다.
더보기
iss: 토큰 발급자(issuer)
sub: 토큰 제목(subject)
aud: 토큰 대상자(audience)
exp: 토큰 만료 시간(expiration), NumericDate 형식으로 되어 있어야 함 ex) 1480849147370
nbf: 토큰 활성 날짜(not before), 이 날이 지나기 전의 토큰은 활성화되지 않음
iat: 토큰 발급 시간(issued at), 토큰 발급 이후의 경과 시간을 알 수 있음
jti: JWT 토큰 식별자(JWT ID), 중복 방지를 위해 사용하며, 일회용 토큰(Access Token) 등에 사용

출처: https://mangkyu.tistory.com/56 [MangKyu's Diary:티스토리]

공개 클레임(Public Claim)

  • 사용자 정의 클레임으로 공개용 정보를 위해 사용한다. 충돌 방지를 위해 URL 포맷을 이용한다.

 

서명(Signature)

헤더와 페이로드를 BASE64 인코딩하고 `.`(마침표)로 연결해 서버가 가지고 있는 비밀키를 추가하고 헤더의 `alg` 알고리즘으로 해싱한 후 인코딩하여 생성한 값이다. 토큰을 검증하는 데 사용한다.

  • 인코딩된 값에 비밀키를 추가하고 해싱하는 이유는 변조를 방지하기 위해서이다. 변조가 되지 않았음을 증명하기 위해 JWT에는 HMAC 해싱 방식을 사용하는데 변조 메세지를 생성한 해싱 값과 HMAC 값이 다르기 때문에 변조되었음을 알 수 있다.

Claim 기반 토큰 Flow

Claim 기반의 토큰의 대표적인 강점은 생성된 토큰을 별도로 서버가 유지할 필요 없다는 것이다.

보통 클라이언트가 API를 요청하기 위해 토큰을 전달하면 서버측에서는 토큰을 가지고 DB를 검색해야한다는 단점이 있는데 그럴 필요 없어진 것이다. 이유는 Claim 자체에 토큰 정보를 담고 있기 때문이다.  다음 Flow를 보자.

서버는 토큰에 사용자 정보나 권한을 넣어 저장하고 인가 처리를 통해 API를 사용한다.

 

Client가 입력 정보를 서버에게 전달하여 토큰을 요청하면 서버는 해당 사용자 정보에 대한 JWT 토큰을 응답하게 된다. 응답받은 토큰 값을 이용해 사용자는 API를 사용할 수 있다. Access Token을 요청하여 생성된 토큰은 API를 사용하기 위해 다음과 같이 Authorization이라는 키의 값으로 사용하게 된다.

{
	"Authorization" : "Bearer {응답받은 토큰 값}"
}

 


JWT 고려사항

JWT엔 장점만 있는 것은 아니다.

DB 부하는 주지 않지만 네트워크 부하는 줄 수 있다.

JWT 자체에 사용자 정보가 담겨 있다. 그렇다면 이 페이로드에 사용자 정보가 많아지면 해당 토큰의 길이도 길어질 것이다. 이는 추후 네트워크 성능 저하의 원인이 될 수 있다.

Payload에 중요한 정보는 넣지 말자.

JWT 자체에 페이로드가 담겨 있고 이는 클라이언트에 저장이 된다. 그리고 단지 BASE64로 인코딩만 되어 있다. 이 말은 즉슨, 중간에 페이로드를 탈취해 디코딩하게 되면 정보를 확인할 수 있다는 것이다. 그래서 민감한 개인 정보는 페이로드에 담지 않는 것이 좋다. 토큰 자체를 암호화하는 방법이 있는데 JSON을 암호화는 스펙으로 JWE(JSON Web Encryption)이 있다.

무효화 하기 어렵다.

세션 방식은 세션 자체를 서버 단에서 삭제하면 되지만 토큰 방식은 삭제가 불가능하기 때문이다. 물론 토큰의 유효 기간이 있다. 그러나 유효기간이 지날 때까지 유효하기 때문에 해커가 유효기간 지나기 전까지 탈취해서 악의적으로 이용할 수 있다.

 


리프레시 토큰 (Refresh Token)

리프레시 토큰은 JWT의 한계점을 조금 해결할 수 있는 수단이다.

리프레시 토큰은 JWT와 다르게 두가지의 토큰을 발급받게 된다.

  • 액세스 토큰(Access Token) : 액세스 토큰은 유효기간이 짧게 설정된다. 그렇다고 너무 짧게 잡으면 자주 로그인 해야한다는 단점이 있고 너무 길면 탈취를 당할 수 있다는 단점이 있다. 적.당.히. 리프레시 토큰을 서버에게 전송하여 액세스 토큰을 다시 발급할 수 있다. 액세스 토큰은 주로 인가 요청을 위해 서버에게 전송되는 토큰을 상태 유지, 권한 요청을 위해 사용된다.
  • 리프레시 토큰(Refresh Token) : 리프레시 토큰은 앞서 설명 했듯이 액세스 토큰을 다시 발급할 수 있게 서버에게 전송하는 토큰이다. 서버 DB와 사용자가 보낸 리프레시 토큰이 일치하는지 검사해 새로운 토큰을 발급해준다.

리프레시 토큰을 이용하면 액세스 토큰의 기간이 짧기 때문에 해커가 탈취를 한다고 해도 오래 유지할 수 없다는 것이다. 뿐만아니라, 토큰을 서버 DB에 저장하게되는데 이는 토큰이 탈취당해도 서버에서 토큰을 삭제해 예방할 수 있다는 것이다. 

 

 

 


☕️ 포스팅이 도움이 되었던 자료

오늘도 저의 포스트를 읽어주셔서 감사합니다.

설명이 부족하거나 이해하기 어렵거나 잘못된 부분이 있으면 부담없이 댓글로 남겨주시면 감사하겠습니다.

 

'Web > Web 이론' 카테고리의 다른 글

전반적인 웹 통신 과정  (0) 2023.12.10
REST(Representational State Transfer) API  (0) 2020.09.21