[인증/보안] JSON Web Token(JWT)
태그: Authentication, JWT, Security, Token
카테고리: 인증 & 보안
JWT란 무엇일까? 왜 쓰는 걸까?
이 글을 작성하게 된 이유는… 이력서에 jwt를 다뤘다고 썼으나 기술 면접에서 ‘jwt를 다뤘다고 하는데 무엇인지 설명할 수 있느냐’에 대해 제대로 답하지 못했기 때문이다…ㅠㅠ;
그래서 이김에 정리하고자 한다! 한 글에 모든 걸 정리할 수는 없으나, jwt부터 시작해서 거꾸로 나아가려고 한다.
- 거꾸로 : 세션, https 등의 개념
토큰 기반 인증(Token-based Authentication)
JWT를 알기 위해서는 토큰이 무엇인지부터 알아야 한다.
그렇기에 토큰 기반 인증에 대해 알아보고자 한다.
토큰 개념
보통 토큰이라고 하면 무엇이 먼저 떠오르는가?
나는 대학생 때 셔틀버스 전용 토큰을 이용하여 셔틀버스를 탔기에, 그에 쓰인 동전이 먼저 떠올랐다.
안타깝게도(?) 졸업할 즈음 그냥 돈으로 바뀌었다.
내가 떠올린 것처럼, 우리 생활에서의 토큰이라 하면 아래의 예시들을 떠올릴 수 있을 것이다.
- 오락실 게임에 사용하는 토큰
- 행사에 입장하기 위해서 주최 측에서 나누어 준 토큰
- 놀이공원에 입장료를 내면 주는 토큰
위 토큰들의 공통점은 ‘지불하면 특정 권리를 받을 수 있다.’이다.
좀 더 쉽게 말하면 ‘나는 돈을 지불했고, 이 시설을 사용할 수 있어!’, 옛날의 ‘마패’ 같은 느낌 같기도 하다.
심지어 토큰에 음각을 새기거나 색깔을 변화시켜서 Silver, Gold, Platinum 등의 ‘혜택이 다른’ 등급을 나눌 수도 있을 것이다.
웹브라우저, 어플리케이션 등을 이용할 때는 로그인을 했느냐 안 했느냐, 구독을 했냐 안 했냐, 관리자냐 사용자냐 등을 구분하곤 한다.
이러한 구분은 곧 그러한 자인지를 인증하는 과정이 필요하며, 그 과정을 위한 인증 정보 또한 보관하고 있어야 한다.
그렇기에, 클라이언트에서 인증 정보를 보관하는 방법으로 토큰 기반 인증이 고안되었다.
클라이언트가 토큰을 가지고 있다면, 보통의 다른(돈을 내지 않은) 유저들과는 다르게 서버에서 제공하는 다양한, 더 프리미엄한 기능을 요청할 수 있을 것이다.
그런데 토큰을 클라이언트에 저장해도 정말 괜찮은 걸까?
나중에 따로 포스팅을 할 거지만, 이후 링크를 붙이도록 하자
클라이언트는 XSS, CSRF 공격에 노출이 될 위험이 있으니 민감한 정보를 담고 있어서는 안 된다.
그렇다면 이상하지 않은가? 클라이언트에서 인증 정보를 보관한다부터 잘못된 것 같아 보인다.
“민감한 정보는 클라이언트에 담으면 안 된다면서, 인증에 사용되는 걸 클라이언트에 담는다고?”라는 의문이 들게 된다.
그러나, 우리가 이걸 괜히 배울 리가 없다.
토큰은 유저 정보를 암호화한 상태로 담을 수 있고, 암호화했기 때문에 클라이언트에 담아도 되며 담을 수 있다!
토큰 기반 인증은 왜 쓸까?
그렇다면!
토큰 기반 인증을 왜 사용하는지는 위에서 답이 됐을 것이다.
나중에 따로 포스팅을 할 거지만, 이후 링크를 붙이도록 하자
세션 기반 인증은 서버(혹은 DB)에 유저 정보를 담는 인증 방식이다. 서버 입장에서는 유저가 민감하거나 제한된 정보를 요청할 때마다 “지금 요청을 보낸 유저에게 우리가 정보를 줘도 괜찮은가?”를 확인하기 위해, 기존에 가지고 있는 세션 값과 일치하는지 확인해야 한다.
매 요청마다 데이터베이스(DB)를 살펴보는 것이 불편하고, 서버 부담도 되며, 이 과정을 줄이고 싶을 것이다.
그럴 때 사용하는 방법이 토큰 기반 인증이고, 그 종류는 무수히 많지만, 대표적으로는 JWT (JSON Web Token)가 쓰인다.
위 토큰의 개념에서 배운 대로, 클라이언트에게 부담을 넘겨준다고도 할 수 있다.
JWT 개념
JSON Web Token의 약자인 JWT.
JSON 포맷으로 사용자에 대한 속성을 저장하는 웹 토큰이다.
JWT 종류
- Access Token
- Refresh Token
Access Token은 보호된 정보들(유저의 이메일, 연락처, 사진 등)에 접근할 수 있는 권한부여에 사용한다.
클라이언트가 처음 인증을 받게 될 때(로그인), Access, Refresh Token 두 가지를 다 받지만, 실제로 권한을 얻는 데 사용하는 토큰은 Access Token이다.
그럼 access token만 있으면 되는 것 아닌가?
왜 두 가지가 필요할까?
권한을 부여받는 데엔 Access Token만 가지고 있으면 된다.
하지만 만약, Access Token을 그 주인(당사자)이 아닌 유저가 얻어낸다면 어떻게 될까?
이 유저는 자신이 본래의 유저인 것처럼 서버에 여러 가지 요청을 보낼 수 있을 것이다.
Example
- 마이페이지 보기(개인정보 확인)
- 그 유저가 그 사이트에서 주로 뭘 하는지 확인
- 돈과 관련된 행위 요청(포인트 사용, 실제 돈 결제 등)
그렇기 때문에 Access Token에는 비교적 짧은 유효 기간을 줘서 탈취되더라도 오랫동안 사용할 수 없도록 하는 것이 좋다.
Access token의 유효기간이 만료된다면 Refresh Token을 사용하여 새로운 Access Token을 발급받는다.
이때 유저는 다시 로그인할 필요가 없다.
Refresh Token도 탈취당할 수 있지 않나?
유효기간이 긴 Refresh Token마저 주인이 아닌 유저가 얻어낸다면 큰 문제가 될 것이다.
상당히 오랜 기간 동안 Access Token이 만료되면 다시 발급받으며 유저에게 피해를 입힐 수 있기 때문…
그래서 유저의 편의보다 정보를 지키는 것이 더 중요한 웹사이트들은 Refresh Token을 사용하지 않는 곳이 많다고 한다.
세상에 완벽한 보안은 없기 때문에 각 방법들의 장단점을 참고하며 필요에 맞게 사용해야 한다.
JWT 구조

JWT는 위 그림과 같이 . 기준으로 세 부분 나눠져 있다.
- Header
Header는 이것이 어떤 종류의 토큰인지(지금의 경우엔 JWT), 어떤 알고리즘으로 Sign할지가 적혀 있다. JSON Web Token이라는 이름에 걸맞게 JSON 형태를 볼 수 있다.1 2 3 4
{ "alg": "HS256", "typ": "JWT" }
이 JSON 객체를
base64방식으로 인코딩하면 JWT의 첫 번째 부분이 완성된다.
Base64인코딩이 무엇인지 추후 포스팅하도록 하자. 이후 링크를 붙이도록 하자 - Payload
Payload에는 정보가 담겨 있다. ‘어떤 정보에 접근 가능한지’에 대한 권한을 담을 수도 있고, ‘사용자의 유저 이름’ 등 필요한 데이터는 이곳에 담아 Sign시킨다. Payload에는 민감한 정보는 되도록 담지 않는 것이 좋다.(ex. 비밀번호 등)1 2 3 4 5
{ "sub": "someInformation", "name": "kimcoding", "iat": 151623391 }
Header 부분과 마찬가지로, 위 JSON 객체를
base64로 인코딩하면 JWT의 두 번째 블록이 완성된다. - Signature
base64로 인코딩된 첫 번째(Header), 그리고 두 번째 부분(Payload)이 완성되었다면, 원하는 비밀 키(암호화에 추가할 salt)를 사용하여 암호화한다.base64인코딩을 한 값은 누구나 쉽게 디코딩할 수 있지만, 서버에서 사용하고 있는 비밀키를 보유한 게 아니라면 해독해 내는데 엄청난 시간과 노력이 들어간다!
예를 들어, 만약 HMAC SHA256 알고리즘(암호화 방법 중 하나)을 사용한다면 signature는 아래와 같은 방식으로 생성될 것이다.
1
HMACSHA256(base64UrlEncode(header) + '.' + base64UrlEncode(payload), secret);
댓글남기기