IT 개발노트
JWT (JSON Web Token), Refresh Token 본문
JWT의 개념
JWT(JSON Web Token)은 토큰 기반 인증에서 사용하는 대표적인 토큰이다. JWT는 총 세 부분으로 이뤄져 있다. 헤더(Header), 페이로드(Payload), 서명(Signature)이 바로 그것이다. 각 부분을 Base64로 인코딩하고 이들을 마침표(.)를 이용하여 차례대로 연결하면 그것이 곧 JWT이다. 다음 그림에서 빨간색 부분이 헤더, 보라색 부분이 페이로드, 하늘색 부분이 서명이다.
1. 헤더 (Header)
헤더는 총 두 가지 정보를 갖는 JSON 값이다. 하나는 토큰의 타입을 지정하는 type, 다른 하나는 서명 계산 및 토큰 검증에 사용할 암호화 알고리즘을 지정하는 alg이다. JWT의 경우에는 type이 무조건 'JWT'이고, alg는 HS256(SHA256) 혹은 RSA 등으로 지정된다. alg에 지정되는 알고리즘을 이용하여 서명을 계산하고 토큰을 검증하는 원리에 대해서는 뒤에서 다룬다.
- type : 토큰의 타입 (무조건 'JWT')
- alg : 서명 계산 및 토큰 검증에 사용할 암호화 알고리즘 (EX. HS256(SHA256), RSA 등)
2. 페이로드 (Payload)
로그인한 사용자의 상태 정보를 갖는 JSON 값이다. 누가 누구에게 발급한 토큰인지, 유효 기한은 언제까지인지, 서비스가 이 토큰을 통해 사용자에게 공개하고자 하는 정보들은 무엇인지 등이 담긴다. 이처럼 토큰에서 사용하는 각 정보의 조각을 클레임(Claim)이라고 부른다. 그리고 로그인한 사용자의 상태 정보가 토큰 자체에 담겨 있기 때문에, 이러한 유형의 토큰을 Self-Contained 토큰이라고 부른다. 서버는 이러한 유형의 토큰을 받으면 그 토큰 자체로부터 사용자의 상태 정보를 얻을 수 있기 때문에 불필요한 데이터베이스 쿼리 등을 최소화시킬 수 있다.
3. 서명 (Signature)
헤더와 페이로드를 Base64로 인코딩하고, 이 둘을 마침표(.)로 연결한 것을 서버의 비밀 키와 함께 (헤더의 alg에 지정된) 암호화 알고리즘에 넣어서 돌리면 서명을 얻을 수 있다. 암호화 알고리즘 특성상, 헤더나 페이로드가 아주 조금만 바뀌어도 서명은 크게 달라진다. 서명은 서버가 토큰의 유효성을 검증하는 데 사용한다. 그 과정은 다음과 같다.
- 서버가 클라이언트로부터 토큰을 받는다.
- 해당 토큰의 헤더, 페이로드와 자신의 비밀 키를 이용하여 서명을 계산한다.
- 해당 토큰의 서명이 방금 계산한 값과 동일한지 검사한다.
- 만약 동일하지 않다면 요청을 거부하고, 동일하다면 해당 토큰의 유효 기한을 확인한다.
- 해당 토큰의 유효 기한이 아직 지나지 않았다면, 요청이 허가된다(= 인가).
변조 방지
무결성을 보장하는 방법 중 많이 사용되는 방법이 서명 (Signature)이나 HMAC (Hash-based Message Authentication) 을 이용한다.
HMAC 방식은 원본 메시지에서 해쉬값을 추출한 후, 이를 비밀키를 이용해서 복호화 시켜서 토큰의 뒤에 붙인다.
누군가 이 메시지를 변조 했다면, 변조 된 메시지에서 생성한 해쉬값과 토큰 뒤에 붙어있는 HMAC 값이 다르기 때문에 메시지가 변조되었음을 알 수 있다. 다른 누군가가 메시지를 변조한 후에, 새롭게 HMAC 값을 만들어내려고 하더라도, HMAC는 비밀키를 이용해서 암호화 되었기 때문에 비밀키를 알 수 없는 이상 HMAC를 만들어 낼 수 없다.
JWT의 취약점
JWT는 상대적으로 보안이 취약한 클라이언트 단에 저장이 된다. 따라서 서버 단에 저장되는 경우보다 더욱 탈취당하기 쉽다.
JWT의 페이로드를 Base64로 디코딩만 하면 해당 사용자의 정보를 손쉽게 얻어낼 수 있다. 따라서 JWT의 페이로드에는 필요한 최소한의 정보만을 담고, 지나치게 민감한 개인정보 등은 담지 않도록 주의해야 한다.
서버는 한 번 발급한 JWT의 경우 그 유효 기한이 지나기 전까지는 유효한 토큰으로 판단한다는 문제가 발생한다. 따라서 한 번 JWT가 탈취당하면 해커는 그 JWT의 유효 기한이 지나기 전까지 마음껏 악의적인 동작을 수행할 수 있게 된다.
JWT의 취약점 보완하기 : 리프레시 토큰 (Refresh Token)
리프레시 토큰을 사용한다는 것은 서버가 인증 완료 시 다음과 같은 두 종류의 토큰을 발급한다는 것을 의미한다.
- 액세스 토큰(Access Token) : 우리가 지금까지 다룬, 인가가 필요한 요청에 실어 서버에게 전송하는 토큰을 의미한다. 즉, 현재 로그인된 사용자의 상태를 기억하며 로그인 상태 유지 및 권한 검사를 위해 사용되는 토큰이다.
- 리프레시 토큰(Refresh Token) : 이미 발급받은 액세스 토큰을 다시 발급받기 위해 서버에게 전송하는 토큰을 의미한다. 말 그대로 액세스 토큰을 갱신(Refresh)하는 것이다.
액세스 토큰은 기본적으로 유효 기간을 굉장히 짧게 설정한다. 이때 만약 리프레시 토큰이 없다면, 로그인을 한 지 얼마 지나지 않아 유효 기한이 지나 다시 로그인을 해야 하는 상황이 반복될 것이다. 그러나 리프레시 토큰이 있기 때문에, 금방 액세스 토큰의 유효 기한이 지나더라도 리프레시 토큰을 서버에게 전송하면 즉시 액세스 토큰을 재발급받을 수 있다. 따라서 다시 로그인을 할 필요가 없다. 이를 위해, 일반적으로 리프레시 토큰은 액세스 토큰보다 유효 기간이 훨씬 더 길게 설정된다. 만약 리프레시 토큰의 유효 기한마저 지난다면 그때는 정말로 로그인을 다시 해야 할 것이다. 즉, 실제 로그인 상태 유지 기간은 리프레시 토큰의 유효 기간이 결정한다.
리프레시 토큰은 인증 완료 시 발급되어, 서버 단의 데이터베이스에 사용자별로 저장된다(물론 액세스 토큰처럼 클라이언트 단에도 저장이 된다). 만약 액세스 토큰이 만료되어 리프레시 토큰을 서버에게 전송하면, 서버는 해당 사용자의 리프레시 토큰을 데이터베이스에서 꺼내온 후 클라이언트가 전송한 것과 일치하는지 검사한다. (액세스 토큰의 페이로드를 통해 어떤 사용자인지 식별이 가능하다.) 만약 일치한다면 액세스 토큰을 새로 발급해준다.
리프레시 토큰 (Refresh Token)의 활용
리프레시 토큰을 사용하는 가장 큰 이유는 바로 액세스 토큰의 유효 기간을 짧게 유지시키는 것이다. 그래야 액세스 토큰이 탈취당하더라도 해커가 그 토큰을 오래 사용할 수 없기 때문이다. 그 유효 기간이 길수록 해커는 해당 토큰을 더 자유롭게 쓸 수 있을 것이다.
유효 기간을 짧게해 피해를 최소화하자.