많은 사람들이 인증/인가의 방법으로 JWT를 사용하고 있다. 나 또한 JWT를 사용했지만, 내가 왜 JWT를 사용했을까?라는 생각을 깊게 해보지 못했던 것 같다. 이번 기회에 그 질문에 대해 깊게 생각해보고 정리를 해보려 한다.
💡 JWT(JSON Web Token) - 유저를 인증하고 식별하기 위한 토큰 기반 인증(Token based Authentication)
먼저 위의 질문에 대해 답을 하기 위해 차근차근히 단계를 밟으며 접근해보도록 하겠다.
보통 서버가 클라이언트 인증을 확인하는 방식으로 쿠키,세션,토큰 이렇게 3가지 방식이 있다고 한다. 토큰 기반 인증 방식인 JWT를 알기 위해 쿠키가 무엇인지, 세션은 또 무엇인지 알 필요가 있어보인다.
웹에서는 HTTP방식을 통해 서로 소통하고 데이터를 주고 받는데 그 HTTP는 stateless라는 특징이 존재한다. 문자그대로 “상태가 없음 → 상태를 알 수 없음”을 뜻한다. 각각의 요청/응답은 모두 독립적이기에 과거에 어떤 통신을 했었는지 상태를 전혀 알 수 없다. 그렇기에 통신을 할 때마다 필요한 정보를 모두 담아서 요청을 해야한다. 하지만 그렇게 되었을때 사이트에서 한번 로그인을 했어도 다음 페이지로 넘어가서 상품을 구매한다던지 다른 요청을 하게 되면 그 전 통신의 기록,상태는 알 수 없기에 매번 로그인을 해서 인증을 받는 불편한 일이 생길 것이다.
바로 이런 불편함을 해결하기 위해 일부 정보들에 대해서는 stateless가 아닌 stateful하게 상태를 유지할 수 있게 Session, Cookie, 그리고 Token과 같은 기술이 나온 것이다. .
1. Cookie
- ⭐️해당 웹사이트의 서버를 통해 클라이언트에서 정보등을 저장하고 관리한다⭐️
- HTTP의 stateless한 특성 때문에 클라이언트의 정보들을 기억하지 못하지만 쿠키를 통해 그 정보들을 저장/연결할 수 있다.
- 인증방식
- 서버에서는 HTTP Response Header에 Set-Cookie 속성을 이용하여 클라이언트에 Cookie를 제공하여 저장하게 하고, 클라언트는 HTTP Request에 저장된 Cookie를 함께 전달하여 이전의 통신에서 사용된 정보들을 파악할 수 있다.
- A cookie is a small piece of data stored on a user's device by a web browser, usually containing information about the user or their activity on a website. Cookies can be used to remember user preferences, track user behavior, and maintain a user's logged-in status across multiple sessions ➡️ 영어가 보다 더 잘 의미를 표현하고 있는 것 같다.
- 특징
- 클라이언트에서 저장한 쿠키 값을 그대로 보내기 때문에 보안에 취약하다
- 쿠키에는 개당 4Kbyte를 넘길 수 없다는 용량제한이 있어 많은 정보를 담을 수 없다.
- 브라우저마다 쿠키에 대한 형태가 다르기에 브라우저간 공유가 불가능하다.
2. Session
- 위에서 언급한 쿠키의 안정성 문제로 인해 ⭐️ 민감한 정보는 클라이언트에 보내지 않고 서버에서 저장하고 관리한다 ⭐️
- 동일한 클라이언트(사용자)가 브라우저를 통해 웹 서버에 접속한 시점으로부터 브라우저를 종료하여 연결을 끝내는 시점 동안에 들어오는 일련의 request를 하나의 상태로 보고, 그 상태를 일정하게 유지하여 클라이언트와 웹 서버가 논리적으로 연결된 상태를 뜻한다.
- 인증방식
- 서버는 Session에 대한 정보를 저장하고 클라이언트에게는 Sesssion을 구분할 수 있는 ID를 부여하는데 이것을 Session ID라고 한다.이 session id는 브라우저의 쿠키에 저장되고 클라이언트는 Request를 보낼 때 해당 Session ID를 쿠키에 담아 함께 보냄으로써, 클라이언트의 상태를 비교하여 인증을 수행한다.
- 클라이언트는 정보를 교환하여 사용자 활동을 추적하고 사용자의 상태를 유지한다. 즉 stateful하게 통신할 수 있게 된다.(상태유지) → 서버가 연결되어 해당 서버가 멈추거나 문제가 생겨서 다른 서버를 사용하게 되면 그 서버는 상태값들을 가지고 있기 때문에 문제가 발생한다 → scale out에 있어 어려움이 있다
- 특징
- 서버에서 세션 저장소를 만들어 관리하기에 상대적으로 안전하지만 사용자 수가 증가할 수록 서버에 가해지는 부하가 증가한다.
- Session ID에 유의미한 개인정보를 담고 있지는 않지만 그 자체를 탈취당하면 실제 클라이언트를 구별해낼 수 없다(올바른 인증을 할 수 없다)
- 모바일기기와 웹에서의 중복로그인 처리가 되지 않는다.
3. Token
- 인증방식
- 클라이언트가 서버에 접속을 하면 서버에 session을 저장하는게 아니라 클라이언트의 식별정보를 가지고 있는 Token을 발급하여 response의 body에 담아 클라이언트에게 보내고 클라이언트는 그 Token은 클라이언트단에 저장하고 request할 때마다 저장된 Token을 request의 header에 포함시켜 보낸다. 서버는 header의 token의 일치여부를 체크하여 인증 과정을 처리한다.
- 특징
- token을 사용자 측에서 저장/관리 하기 때문에 서버의 부담을 덜 수 있다.
- 서버의 scale out에 용이하다(다른 서버에서 상태값들을 가지거나 이럴 필요가 없으니까)
- 토큰을 탈취당하면 대처가 어렵기에 토큰의 만료시간을 짧게하여 극복할 수 있다
💡 Token과 Session을 이용한 Session based Authorization은 stateful하여서 여러장점이 존재하지만 웹과 서버가 연결되어있기에 확장성이 낮고 어렵다는 단점이 있다. 그래서 stateless 특징을 유지하면서도 로그인 상태 유지를 가능하게 하는 것이 바로 JWT이다.
JWT
(JWT가 무엇일까?가 아니라 왜 사용했을까?의 주제로 설명되기에 보다 자세한 설명은 생략하도록 하겠습니다!)
- 구조
- Header 에는 JWT 에서 사용할 타입과 해시 알고리즘의 종류가 담겨있으며, Payload 는 서버에서 첨부한 사용자 권한 정보와 데이터가 담겨있다. 마지막으로 Signature 에는 Header, Payload 를 Base64 URL-safe Encode 를 한 이후 Header 에 명시된 해시함수를 적용하고, 개인키(Private Key)로 서명한 전자서명이 담겨있다.
🔥 JWT는 인증(서명)이 목적이다. 정보 보호가 아니다! 🔥
➡️ 토큰 안에 어떤 정보가 들어있는지 아는게 중요한게 아니라 해당 토큰이 유효한 토큰인지를 확인하는 것이 중요하다
암호화를 하면 보안적으로 무조건 좋을 것이라는 나의 편협한 사고방식 때문에 JWT를 이해하는데 혼란을 주었다.
그전까지만해도 secret key를 통해 header와 payload,signature를 secret key로 암호화한다고 위 그림처럼 잘못 생각하고 있었다… 분명 암호화를 했는데 JWT.IO 에 가면 너무나 쉽게 복호화가 가능한 것을 보고 “이게 왜 보안에 좋다는거지?”라고 생각했었다.
그런데 그게 아니라 암호화는 signature에서만 발생하는 것이였다. Header와 Payload는 단순히 인코딩된 값이기에 3자가 복호화 및 조작할 수 있는게 맞았고 Header에서 정의한 해시 알고리즘에 의해 암호화된 Signature를 통해 토큰 전체의 위변조 여부를 확인하는 것이였다.
- 특징
- 정보보호가 목적이 아니기에 단순히 인코딩 된 payload에는 민감한 정보를 포함해서는 안된다
- 세션과 달리 서버는 stateless의 상태여서 서버 확장성에 유리하다
- Extensibility: 토큰 기반 인증은 토큰을 통해 권한의 범위를 지정할 수 있다. OAuth의 경우 Kakao,Naver,Google과 같은 소셜 계정을 이용하여 다른 웹서비스에서도 로그인을 할 수 있다.
- 모바일 앱 환경에서도 잘 동작한다(모바일은 세션을 사용할 수 없다)
- ⭐️ 토큰은 서버가 아닌 클라이언트 측에서 저장/관리 하기에 토큰 자체를 탈취 당하면 대처하기 어렵다
토큰의 탈취 위험성 때문에 JWT는 보통 Access Token, Refresh Token 이렇게 이중으로 나누어 인증을 하는 방식을 취한다고 한다.
(사실 그전에 프로젝트를 할 때 access token으로만 인증했던 것이 생각나서 refresh token을 추가하여 인증을 하게끔 리팩토링을 하고 있다. 끝나는대로 정리해서 올려보도록 하겠다..)
마치며
이 글의 제목의 답을 짧게 해보자면 "나는 사용자가 매번 로그인해야하는 불편을 없애기 위해 사용자 정보를 안전하면서도 효과적으로 서버에게 보내 인증을 받기 위해 JWT를 사용했다"라고 할 수 있을 것 같다.
마지막으로, JWT에 대해 공부를 하며 내가 사용했던 코드의 문제점을 발견하고 동시에 리팩토링을 하고 있다. 기능과 목적을 알고 코드들을 구현하니까 확실히 한줄한줄 코드를 정확하게 생각하며 적을 수 있게되는 것 같아 기분이 좋다 🤩
P.S 혹시라도 잘못된 내용이나 추가해야할 내용들이 있다면 언제든지 알려주시면 감사하겠습니다..공부하며 기록하다보니 틀린 내용이 있을 수도 있습니다..
'TIL' 카테고리의 다른 글
Libuv 라이브러리(feat. 이벤트 루프) - 3 (0) | 2023.03.24 |
---|---|
V8엔진 구조 및 작동 방법 - 2 (0) | 2023.03.23 |
나는 왜 Node.js를 사용했을까? - 1 (0) | 2023.03.15 |
Process & Thread (0) | 2023.03.13 |
API가 도대체 뭐야? 이걸로 끝내자 (0) | 2023.03.10 |