JWT & 토큰 기반 인증 시스템

tiki·2021년 6월 14일
0

CS

목록 보기
2/3
post-thumbnail
post-custom-banner

👮‍♀️ 회원 인증

웹사이트를 만들어도...
앱을 만들더라도...
회원 기능은 필수일 정도로 꼭 필요한 기능입니다.

그렇다면 datebase와 server, 클라이언트와의 적당한 코드로 회원가입을 구현하는 것은 쉽습니다. 회원가입 form을 통해서 가입하고자 하는 회원의

{
  "id": 2young@gmail.com,
  "password": q1w2e3r4
}

이러한 형태로 json 데이터를 받아서 DB에 저장하면 끝! 입니다.

하지만.. 현실은 이렇게 쉽지 않습니다. 회원가입 이후 회원이 로그인한 후 회원이 필요한 기능을 수행할 때마다 서버에서는 회원이 맞는지 검증하는 시스템이 있어야합니다.

그 기능이 없다면 아무나 정보를 수정하고 취득할 수 있으니깐요 ㅠㅠ

🎬 Session 기반 인증 방식

그리하여 회원이 로그인을 하고 나면 해당 회원을 유지시켜주고 기능을 사용할 때 해당 회원임을 인증해주는 시스템이 등장했습니다.

✏️ 세션 기반 인증 방식

JWT 토큰 기반 인증 방식이 나오기 전 세션 기반 인증 방식이 주로 이용됐다.

인증 방식

  1. 클라이언트에서 로그인인 요청이 들어옴
  2. 로그인에 성공하면 서버가 유저 세션을 만들고 메모리나 데이터베이스에 저장
  3. 서버가 클라이언트에게 세션 ID를 보낸다.
  4. 클라이언트의 브라우저에 세션의 ID만 쿠키에 저장하게 한다.
  5. 세션 데이터가 서버의 메모리에 저장되므로, 확장 시 모든 서버가 접근할 수 있도록 별도의 중앙 세션 관리 시스템이 필요하다.

단점

  • 중앙 세션 관리 시스템이 없으면, 시스템 확장에 어려움이 생긴다.
  • 중앙 세션 관리 시스템이 장애가 일어나면, 시스템 전체가 문제가 생긴다.
  • 만약 메모리에 세션 정보가 들어있다면, 메모리가 많이 사용될 수 있다.

💳 JWT(토큰) 기반 인증 방식

오늘의 주된 내용은 JWT에 대한 내용이다.

🔎 JWT 란?

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

  • JWT는 Claim 기반 방식을 사용한다. JWT은 의미있는 토큰 (사용자의 상태를 포함) 으로 구성되어 있기 때문에, Auth Server에 검증 요청을 보내야만 했던 과정을 생략하고 각 서버에서 수행할 수 있게 되어 비용 절감 및 Stateless 아키텍처를 구성할 수 있다.

🔎 JWT의 구성

JWT는 .을 구분자로 3가지 문자열로 구성됩니다.

Header(헤더), Payload(내용), Signature(서명)
ex) xxxxxx.yyyyy.zzzzz

전송을 위해서 Base64로 인코딩되어 있습니다. 하지만 모두 암호화 된것은 아니기 때문에 민감한 정보를 담을 때는 주의해야합니다!!

Header와 Payload는 디코딩을 하면 평문으로 해독이 가능합니다. 따라서 인증정보, 비밀번호와 같은 민감한 정보를 넣으면 안됩니다. 그러나 Signature는 복호화할 수 없습니다.

Header(헤더)

헤더는 JWT 토큰을 어떻게 해석해야 하는지를 알려주는 부분입니다.
typ, alg 두 가지 정보로 구성됩니다.
JWT 헤더를 디코딩했을 때 보여지는 예시입니다.

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

alg: 알고리즘 장식을 지정, 서명 및 토큰 검증에 사용합니다.
typ: 토큰의 타입을 지정합니다.

Payload(내용)

기본적으로 json의 형태로 name / value 쌍으로 이루어져 있습니다.

Payload에 있는 속성들을 Claim이라고 합니다.

Payload에 등록된 클레임들은 종류에 따라서 다음과 같이 구분됩니다.

  • 등록된 클레임(Registered claims)
  • 공개 클래임(Public claims)
  • 비공개 클래임(Private claims)

1. 등록된 클레임(Registered Claim Names)

등록된 클레임들은 서비스에서 필요한 정보들이 아닌, 토큰에 대한 정보들을 담기위하여 이름이 이미 정해진 클레임들입니다. 등록된 클레임의 사용은 모두 선택적 (optional)이며, 이에 포함된 클레임 이름들은 다음과 같습니다

{ 
  	"ss(issuer)": 토큰 발급자,
  	"sub(subject)": 토큰 제목,
  	"aud(audience)": 토큰 대상자,
  	"exp(expiration)": 토큰 만료 시간 ex) 1480849147370,
  	"nbf(not before)": 토큰 활성 날짜 - 이 날이 지나기 전에 토큰은 활성화되지 않습니다.
	"iat(issued at)":  토큰이 발급된 시간 - 토큰 발급 이후의 경과 시간을 알 수 있습니다
	"jti(JWT ID)": JWT 토큰 식별자 - 중복 방지를 위해 사용
}

2. 공개 클래임(Public Claim)

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

{
  "https://velog.io/@yh20studio": true
}

3. 비공개 클래임(Private Claim)

등록된 클레임도 아니며, 공개 클래임도 아닌 서버와 클라이언트 사이에 임의로 지정한 정보를 저장하기 위해 만들어진 사용자 지정 클레임입니다.

{
  "username": "2young"
}

비공개 클래임을 조심해서 사용하지 않으면 등록된 클래임과 공개 클래임과 다르게 충돌이 일어날 수 있기에 주의해야 합니다.

Signature(서명)

헤더와 페이로드는 암호화 한 것이 아니라 단순히 JSON 문자열을 base64로 인코딩한 것입니다. 누구나 디코딩을 한다면 헤더와 페이로드의 내용을 볼 수 있습니다.
누군가 JWT를 탈취하여 수정한 후 서버로 보낼 수 있습니다. 이 경우에 대비해 다른 사람이 위변조 했는지 검증하기 위한 부분입니다.
서명은 헤더의 인코딩 값, 정보의 인코딩 값을 합친 후 비밀키로 해쉬를 하여 생성합니다.

이 서명에 사용되는 비밀키는 외부에 노출되지 않도록 주의해야합니다.

비밀키가 외부에 노출된다면 누구나 서버의 인증을 통과하는 signature를 만들 수 있기 때문에 인가되지 않은 요청들이 통과될 수 있습니다.

⁉️ Access Token & Refresh Token

⏰ 토큰의 만료시간

토큰 방식을 이용하여 회원인증을 구현하게 된다면 몇가지 문제가 생기게 된다.

토큰을 한번 발행하고 클라이언트 서버에 보내고 나면 이후에 서버에서는 토큰을 파괴시킬 방법이 존재하지 않는다. 보통 JWT 토큰은 클라이언트의 로컬 Storage에 저장하고 사용하게 되는데 이때 혹시나 토큰의 탈취 현상이 일어난다면 해당 토큰을 서버에서 제어할 수 없기 때문에 문제가 발생하게 된다.

따라서 토큰을 발행할 때는 무조건! 만료시간을 넣어줘야 한다.

  • 그렇다면 보안을 위해서 만료시간을 짧게 해야할까? 막연하게 짧게 설정한다면 토큰이 만료될 때마다 사용자는 다시 로그인을 해줘야하거나 서버는 토큰을 매번 발행해야하는 번거로움이 발생할 것이다.

  • 그렇다고 사용의 편리함과 서버의 부하를 줄이기 위해서 토큰의 만료시간을 무제한으로 늘릴 수는 없다. 보안에 취약하기 때문이다.

이를 적당히 보완하며 중간 지점을 맞추기 위해서 등장한 것이
Access Token과 Refresh Token 이다.

🔑 Access Token

  • 리소스에 직접 접근할 수 있도록 해주는 정보만을 갖고 있습니다.
  • Refresh Token에 비해서 짧은 만료 기간을 갖습니다.

🔑 Refresh Token

  • 새로운 Access Token을 발급받기 위한 정보를 갖습니다.
  • 클라이언트가 Access Token이 없거나 만료되면 Refresh Token을 통해 Server에 요청해서 새로운 Access Token을 발급 받을 수 있습니다.
  • Access Token에 비해서 긴 만료 기간을 갖습니다.
  • 외부에 노출되지 않도록 관리를 위해서 데이터베이스에 저장한다.

🔗 JWT 인증과정

✏️ 요청의 Header

JWT는 일반적으로 클라이언트와 서버, 서비스와 서비스 사이 통신 시 권한 인가(Authorization)를 위해 사용하는 토큰이며, api 요청의 header에 다음과 같이 넣어서 이용한다.

{
	"Authorization" : "Bearer jwt"
}

👍 JWT(토큰)의 장점

무상태성(Stateless) & 확장성(Scalability)

토큰은 클라이언트 측에 저장되기 때문에 서버는 완전히 Stateless하며, 클라이언트와 서버의 연결고리가 없기 때문에 확장하기에 매우 적합하다.

만약 사용자 정보가 서버 측 세션에 저장되고, 중앙 세션 관리 시스템이 없는 경우에 서버를 확장하여 분산처리 한다면, 해당 사용자는 처음 로그인 했었던 서버에만 요청을 받도록 설정을 해주어야 한다.

하지만 토큰을 사용한다면 어떠한 서버로 요청이 와도 상관이 없다. 비밀키 값만 서버들이 알고 있다면 처리하는데 문제가 없기 때문이다.

보안성

클라이언트가 서버로 요청을 보낼 때 더 이상 쿠키를 전달하지 않으므로, 쿠키 사용에 의한 취약점이 사라지게 된다. 하지만 토큰 환경의 취약점이 존재할 수 있으므로 이에 대비해야 한다.

여러 플랫폼 및 도메인

서버 기반 인증 시스템의 문제점 중 하나인 CORS를 해결할 수 있는데, 애플리케이션과 서비스의 규모가 커지면 여러 디바이스를 호환시키고 더 많은 종류의 서비스를 제공하게 된다. 토큰을 사용한다면 어떤 디바이스, 어떤 도메인에서도 토큰의 유효성 검사를 진행한 후에 요청을 처리할 수 있다.

🎛 어플리케이션, 웹 모두 컨트롤이 가능하다는 뜻이다!

확장성(Extensibility)

토큰에 선택적인 권한만 부여하여 발급할 수 있다.

Ex) 아마존에 구글 계정으로 로그인 할 수 있어도 아마존이 로그인한 구글 계정의 메일을 읽을 수 없습니다.

OAuth의 경우 페이스북, 구글, 카카오와 같은 소셜 계정을 이용하여 다른 웹 서비스에서도 로그인 할 수 있습니다.

👎 JWT(토큰)의 단점

Self-contained

  • 토큰 자체에 정보를 담고 있으므로 양날의 검이 될 수 있다. 정보를 얻기 쉬운 반면 탈취된다면 보안에는 취약할 수 있기 때문이다.
  • 토큰의 페이로드(Payload)에 3종류의 클레임을 저장하기 때문에, 정보가 많아질수록 토큰의 길이가 늘어나 네트워크에 부하를 줄 수 있다.

암호화

  • 페이로드(Payload) 자체는 암호화 된 것이 아니라, BASE64로 인코딩 된 것이다. 중간에 Payload를 탈취하여 디코딩하면 데이터를 볼 수 있으므로, JWE로 암호화하거나 Payload에 중요 데이터를 넣지 않아야 한다.
  • signature을 생성하는 비밀키가 외부에 노출되지 않아야한다. 따라서 서버를 확장하고 구성함에 있어서 만약 외부키를 잃어버리거나 노출하게 된다면 시스템이 중단될 수도 있다.

Stateless

  • JWT는 상태를 저장하지 않기 때문에 한번 만들어지면 제어가 불가능하다. 즉, 토큰을 임의로 삭제하는 것이 불가능하므로 토큰 만료 시간을 꼭 넣어주어야 한다.

📍 결론

사용자 인증 시스템은 웹 혹은 앱 서비스를 하는데 있어서 거의 필수라고 할 수 있습니다. 이에 세션을 이용한 방법과 JWT 토큰을 이용한 방법이 주로 이용되고 있습니다. 이때 중앙 세션 처리 시스템을 이용해서 처리하는 방법이 어려운 상황이라면, 혹은 웹사이트, 앱을 동시에 처리하는 서버를 구성하기 위해서는 JWT 토큰 방식을 이용한 인증이 더 장점이 많은 것 같습니다.

📎 참고

JWT를 소개합니다.
JWT란?
[Server] JWT(Json Web Token)란?
[JWT] JSON Web Token 소개 및 구조

profile
하루하루 조금씩 발전하려는 개발자 입니다.
post-custom-banner

0개의 댓글