티스토리 뷰

Programming

쉽게 알아보는 서버 인증 2편(Access Token + Refresh Token)

이호연 자유로운 오랑우탄 2018.07.29 02:23



<26$ JWT 티셔츠 >




안녕하세요! 이전 포스팅에는 크게 세션/쿠키 인증, 토큰 기반 인증(대표적으로 JWT)에 대하여 알아보았습니다. 저희가 앱, 웹 혹은 서버 개발을 하면서 꼭 사용하게 되는 인증(Authorization)은 아주 중요합니다. 만일 설계를 잘못했을 시, 회원들의 정보 유출이 일어날 수도 있으니까요.


이번 포스팅에서는 기본 JWT 방식의 강화버전인 Access Token & Refresh Token 방식에 대해 알아보겠습니다. 




Refresh Token?

Access Token(JWT)를 통한 인증 방식의 문제는 만일 제 3자에게 탈취당할 경우 보안에 취약하다는 점입니다.

유효기간이 짧은 Token의 경우 그만큼 사용자는 로그인을 자주 해서 새롭게 Token을 발급받아야 하므로 불편합니다. 그러나 유효기간을 늘리자면, 토큰을 탈취당했을 때 보안에 더 취약해지게 됩니다. 

이때 “그러면 유효기간을 짧게 하면서  좋은 방법이 있지는 않을까?”라는 질문의 답이 바로 "Refresh Token"입니다. 


Refresh Token은 Access Token과 똑같은 형태의 JWT입니다. 처음에 로그인을 완료했을 때 Access Token과 동시에 발급되는 Refresh Token은 긴 유효기간을 가지면서, Access Token이 만료됐을 때 새로 발급해주는 열쇠가 됩니다(여기서 만료라는 개념은 그냥 유효기간을 지났다는 의미입니다.) 


사용 예를 간단히 들어보겠습니다. Refresh Token의 유효기간은 2주, Access Token의 유효기간은 1시간이라 하겠습니다. 사용자는 API 요청을 신나게 하다가 1시간이 지나게 되면, 가지고 있는 Access Token은 만료됩니다. 그러면 Refresh Token의 유효기간 전까지는 Access Token을 새롭게 발급받을 수 있습니다. 




** Access Token은 탈취당하면 정보가 유출되는건 동일합니다. 다만 짧은 유효기간 안에만 사용이 가능하기에 더 안전하다는 의미입니다. 


** Refresh Token의 유효기간이 만료됐다면, 사용자는 새로 로그인해야 합니다. Refresh Token도 탈취될 가능성이 있기 때문에 적절한 유효기간 설정이 필요해보입니다(보통 2주로 많이 잡더군요)



Access Token + Refresh Token 인증 과정




이번에는 과정이 좀 복잡합니다. 차근차근 하나씩 알아봅시다. 빨간색은 1편의 JWT 인증방식에서 추가로 들어간 과정입니다.


1. 사용자가 ID , PW를 통해 로그인합니다.


2. 서버에서는 회원 DB에서 값을 비교합니다(보통 PW는 일반적으로 암호화해서 들어갑니다)


3~4. 로그인이 완료되면 Access Token, Refresh Token을 발급합니다. 이때 일반적으로 회원DB에 Refresh Token을 저장해둡니다.


5. 사용자는 Refresh Token은 안전한 저장소에 저장 후, Access Token을 헤더에 실어 요청을 보냅니다.


6~7. Access Token을 검증하여 이에 맞는 데이터를 보냅니다.


8. 시간이 지나 Access Token이 만료됐다고 보겠습니다.


9. 사용자는 이전과 동일하게 Access Token을 헤더에 실어 요청을 보냅니다.


10~11. 서버는 Access Token이 만료됨을 확인하고 권한없음을 신호로 보냅니다.


** Access Token 만료가 될 때마다 계속 과정 9~11을 거칠 필요는 없습니다.

 사용자(프론트엔드)에서 Access Token의 Payload를 통해 유효기간을 알 수 있습니다. 따라서 프론트엔드 단에서 API 요청 전에 토큰이 만료됐다면 바로 재발급 요청을 할 수도 있습니다.



12. 사용자는 Refresh Token과 Access Token을 함께 서버로 보냅니다.


13. 서버는 받은 Access Token이 조작되지 않았는지 확인한후, Refresh Token과 사용자의 DB에 저장되어 있던 Refresh Token을 비교합니다. Token이 동일하고 유효기간도 지나지 않았다면 새로운 Access Token을 발급해줍니다.


14. 서버는 새로운 Access Token을 헤더에 실어 다시 API 요청을 진행합니다. 




Refresh Token이 들어가면서 과정이 좀 복잡해졌습니다. 하지만 Access Token의 약점을 보완해주기 때문에 보안이 중요한 프로젝트에서는 사용하기를 권장합니다. 




(큰 장점)


기존의 Access Token만 있을 때보다 안전합니다.



(단점)


1. 구현이 복잡합니다. 검증 프로세스가 길기 때문에 자연스레 구현하기 힘들어졌습니다(프론트엔드, 서버 모두)


2. Access Token이 만료될 때마다 새롭게 발급하는 과정에서 생기는 HTTP 요청 횟수가 많습니다. 이는 서버의 자원 낭비로 귀결됩니다. 




저도 현재 프로젝트에  Access Token + Refresh Token 방식을 도입하였습니다. 처음에 구현하기가 좀 힘들 수 있으나 충분히 가치가 있다고 봅니다. 


다음 포스팅에서는 페이스북, 네이버 로그인에 쓰이는 Oauth에 대해 알아보도록 하겠습니다. Oauth도 Access Token + Refresh Token 방식으로 진행되기 때문에 어렵지 않게 이해하실 수 있을겁니다. 짜이찌엔!

댓글
  • 프로필사진 삼정 페북 링크타고 왔습니다. 1편에 이어서 2편도 유익하게 잘 보았습니다. 감사합니다.~ 2018.07.29 12:10 신고
  • 프로필사진 이호연 자유로운 오랑우탄 잘 읽어주셔서 감사합니다!! 더 유익한 글 올리도록 노력하겠습니다 ㅎㅎㅎ 2018.08.11 23:33 신고
  • 프로필사진 화이트채플 Access token 을 갱신할 때 Refresh Token을 전송하는것은 이해했습니다. 그런데 이 때 굳이 만료된 AccessToken을 같이 보내는 이유가 무엇인지 알 수 있을까요?
    그냥 RefreshToken만 전송해도 될것 같은데 그 이유를 알려주시면 감사드리겠습니다!
    2019.03.12 14:40
  • 프로필사진 이호연 자유로운 오랑우탄 안녕하세요! 답변이 늦었습니다.
    새로운 토큰 발행의 관점에서는 굳이 만료된 Access Token을 보낼 필요는 없습니다!! 다만 이는 조건이 추가된 것이라 보면 됩니다.
    1. Refresh Token만 보내면 그때그때 새로운 Access Token을 발급해준다.
    2. 이전의 사용하던 Token을 같이 보내야만 기존의 사용자라는 걸 인식하고 새 토큰을 발행해 준다.

    2019.03.22 00:37 신고
  • 프로필사진 궁금해요 안녕하세요 잘 봤습니다.
    하나 궁금한점이 있는데 RefreshToken을 저장하는 '안전한 저장소'가 정확히 어떤걸 뜻하나요? RefreshToken의 의의가 AccessToken이 탈취되었을 경우에 악의적인 요청 시간을 최소화 하기 위함이라고 이해했는데, 만약에 웹 환경일 경우 마땅한 저장소가 Refresh, Access 둘다 쿠키 말고는 없는것 같습니다(현재 저도 AccessToken을 쿠키에 저장하고 있습니다). 만일 이런 경우 RefreshToken을 어디에 저장 해야 좋을까요? 만일 RefreshToken도 마찬가지로 쿠키에 저장하고, 쿠키가 탈취되었다고 가정하면, AccessToken이 탈취됨과 동시에 RefreshToken도 같이 탈취되서 RefreshToken의 의의가 사라지게 되는데... 어떻게 해결해야할지 너무 궁금합니다
    2019.04.09 09:58
  • 프로필사진 이호연 자유로운 오랑우탄 확인이 늦었습니다T_T
    안전한 저장소의 개념은 웹에서는 크게 통용되진 않는 것 같습니다. 실제로 Access Token & Refresh Token 방식의 장점 자체가 서버에서 Stateless 하게 인증/인가를 처리할 수 있다는 것이고 이는 서버 운영 관점에서 큰 이점이 있습니다. 하지만 세션보다는 클라이언트에게 보안과 관련된 권한을 더 맡기게 되죠.

    동시에 탈취된다면 결국 똑같습니다. 즉 폰을 털리거나 컴퓨터를 털리면 이는 말짱꽝이죠, 다만 Access Token을 통신할 때만 사용하고 Refresh Token은 어딘가에 숨겨둔다면, 이는 Access Token이 탈취되더라도 Refresh Token의 위치는 알 수 없겠죠.

    그리고 저같은 경우는 웹에서 localStorage에 다가 저장을 하긴 합니다만.. local storage의 경우 XSS, cookie의 경우 CSRF에 대한 위험이 있습니다. 아래 글을 참조해보셔도 좋을 것 같습니다.
    https://github.com/IdentityServer/IdentityServer3/issues/2039
    2019.05.19 00:13 신고
  • 프로필사진 hanumoka 글 잘 읽었습니다. 궁금한것이 있어서 질문드려요.
    accesstoken가 탈취 될 경우를 대비해서 refreshtoken으로 accesstoken을 갱신 하는것은 이해가 되지만, refreshtoken이 탈취 당하면 accesstoken을 재 발급 받을수 있는것 아닌가요? 그렇다면 결과적으로 refreshtoken으로 acesstoken을 갱신하는 구조가 accesstoken 단일 토큰 사용을 사용하는 구조에 비해 어떠한 보안상 이점이 있는지 잘 모르겠습니다.

    accesstoken의 경우 통신시 네트워크에 높은 빈도로 노출되지만, refreshtoken은 accesstoken 갱신시에만 요청되기 때문에 상대적으로 네트워크에 노출될 위험이 적어보이기는 하지만....음.. 모호하네요.


    그리고 refreshtoken을 생성할때 db에 저장하는 이유는 무엇인가요? refreshtoken을 생성할때 서버에서 secretkey로 서명?하기때문에 서버에서는 refreshtoken을 따로 db에 저장하지 않더라도 이 토큰의 유효성을 판단 할 수 있지 않나요?
    2019.05.23 08:58
  • 프로필사진 이호연 자유로운 오랑우탄 안녕하세요^^
    첫 번째 답변으로 우선 Access Token, Refresh Token을 개념적으로 분리해보겠습니다.
    Access Token의 경우 짧은 유효시간을 가지고 있으며 서버 단에서 통제가 불가능합니다.
    Refresh Token 은 Access Token을 재발급받기 위한 Key 입니다. 서버 단에서 통제가 가능하며, 클라이언트 단에서는 안전한 스토리지에 저장됩니다.
    JWT를 사용한 토큰 방식으로 인증/인가를 관리할 수 있다는 건 별다른 관리가 필요없이 토큰의 유효성만 검증하면 되는 쉬운 로직입니다. 이는 확장성이 매우 뛰어나서 요새 서버 운용 방식에 딱 맞는 방식입니다.
    그러나 만일 Access Token만 사용하게 될 경우 위의 설명과 같이 서버 단에서 통제가 불가능하다는 단점이고 이는 치명적으로 보안에 악영향을 끼치겠죠. 따라서 Refresh Token 방식을 도입해서 Token에 대한 관리를 가능하게 하고, 동시에 기존에 사용하던 DB 에 저장함으로서 별다른 메모리 기반 스토리지(Redis, memcached etc)를 이용하지 않아도 되기에 장점을 잃지 않게 될 것입니다.

    두 번째의 경우
    Refresh Token이 탈취당하게 될 경우 해당 토큰의 유효기간만큼 Access Token을 무한정 발급받을 수 있다는 단점이 있습니다. 이 때 탈취된 토큰을 무효화하는 로직이 필요한데 유효성 검사 자체만으론 한계가 있습니다. 따라서 DB나 보관이 가능한 스토리지에 Refresh Token을 저장함으로서 관리를 해줄 수 있습니다(만일 Refresh Token을 지우면 클라이언트에서 보낸 토큰과 일치하지 않으므로 무효화되겠죠?)
    2019.05.25 00:33 신고
댓글쓰기 폼