Access Token의 취약점
Access Token은 암호화 보다는 서명에 초점을 맞춘 토큰이므로 탈취 당할 위험성이 있다. 보통 Access Token에는 중요한 정보를 담지 않아서 탈취당하더라도 별 문제가 없다.
그러나 서비스 이용자에게 권한을 부여하는 경우에는 문제가 될 수 있다.
Access Token은 발급된 이후 서버에 저장되지 않고 토큰 자체로 검증을 하여 사용자 권한을 인증하므로, 기한이 만료되기 전 까지 토큰을 탈취한 사람은 권한 접근이 가능해진다.
Access Token은 발급한 후 삭제가 불가능하므로 토큰 유효기간을 짧게 설정하는 것이 해결책이 될 수 있지만, 그만큼 사용성이 떨어지는 단점이 있다(새로 Token을 발급받기 위해 로그인을 자주 해야함).
⇒ Access Token의 유효기간이 짧을 수록 보안 위험성이 낮아지지만 사용자에게 불편함을 야기한다.
Access Token의 유효기간을 짧게 하면서 보안을 강화할 수 있는 방법
토큰의 종류를 하나 더 추가하여 관리하는 해결법이 있다. Access Token을 발급할 때, Access Token을 재발급 하기 위한 Refresh Token라는 JWT 토큰도 발급해서 같이 전달하는 것이다.
Access Token은 인증을 위한 JWT로 사용하고 보안을 위해 유효기간을 짧게 설정한다.
Refresh Token은 Access Token이 만료되었을 때 새롭게 발급해주는 열쇠 역할을 한다. 그래서 Refresh Token은 상대적으로 유효기간을 길게 설정하고(일반적으로 2주), 서버의 DB에 저장해둔다
Access Token이 만료되어 새로 발급되는 과정을 사용자는 모르므로 사용성 부분이 해결된다(로그인이 계속 되어 있다고 느낀다).
인증 후의 인가 과정에서 서버와 클라이언트 각각의 입장에서는 다음과 같은 프로세스를 진행한다.
- [ 서버 ] Access Token 과 Refresh Token을 발급한 후 DB에 Refresh Token을 저장한다.
- [ 클라이언트 ] 서버에서 전달받은 Access Token과 Refresh Token을 쿠키나 세션 혹은 웹스토리지에 저장한다.
- 요청마다 Access Token을 헤더에 담아서 전송한다.
- Refresh token은 Access token을 재발급할 때만 서버에 전송한다.
기존의 Access Token만 있는 상황과 비교했을 때 훨씬 안전해지지만, 다음과 같은 단점도 있다.
- 복잡한 구현: 검증 프로세스가 길어졌기 때문에 서버 단과 프론트엔드 단 모두에서 구현이 어려워진다.
- 서버의 자원 낭비: Access Token이 만료될 때마다 재발급 요청을 해야하므로 HTTP 요청 횟수가 많아진다.
그런데… 굳이 Refresh Token을 써야하나요 다른 방법은 없나요?
이 파트는 OKKY의 이 질문답변을 참고하여 궁금했던 점들을 정리해두었습니다.
- Access token을 무효화 처리(삭제할 수 있게) 한다면?
- 리소스 서버와 인증 서버가 분리된 경우, 매번 인증서버에 무효화 여부를 물어봐야 하고, 인증 서버가 보틀넥이 된다.
- 그렇지 않은 경우 역시, 토큰 무효화는 DB와 같은 서버 스토리지가 필요하고 따라서 latency에 영향을 준다.
⇒ 따라서 Access token의 무효화는 성능 하락을 야기시키므로, 일반적으로 Access token은 짧은 유효기간을 가지고 별도의 무효화는 지원하지 않는다. 대신 Refresh Token은 긴 유효기간과 무효화를 지원한다.
- Refresh Token이 탈취되어 해커가 Access Toeken을 발급 받는다면?
Access token이 탈취되는 경우에는 서버에서는 아무런 방어를 할 수 없다. 더욱이 토큰이 탈취되었다는 사실조차 모를 수 있다. Refresh token이라는 방어 도구라도 있어야 서버가 해킹된 토큰에 대한 방어 행동을 취할 수 있다.
Refresh Token은 탈취되더라도 Access Token 발급이 진행되는 과정에서 해킹 여부를 검증하는 작업을 서버에서 할 수 있어서(해외 IP 주소 요청 또는 계정 도용으로 신고된 아이디로 요청하는 등) 새로 Access Token을 발급받는 것을 막을 수 있다.
⇒ Refresh Token이 탈취되더라도 서버의 검증으로 AccessToken을 재발급 받는 것을 막을 수 있다.
- 그래도 Access Token이 탈취될 위험성은 남아있잖아요 (HTTP 하이재킹 등..)
그러므로 https와 같은 안전한 전송수단만을 사용하여 클라이언트와 서버 간에 전송되는 데이터를 암호화해야한다.
토큰은 클라이언트의 어디에 저장하고 어떻게 보내는게 좋나요?
Access token은 요청 header의 authorization에 실어야하기 때문에, 프론트 단에서의 접근이 필수적이다(이건 공식 규격이다).
그래서 Access token의 저장 장소도 로컬 스토리지 → 쿠키 → redux 같은 스토어(클로저로 감싸진 지역 변수)방향으로 꾸준히 탈취하기 어렵게 변경되어 왔다.
반면 Refresh Token의 경우 그럴 이유가 없어서 http only 쿠키로 넘겨줄 경우 js를 통한 접근이 불가능해지므로 보안이 강화된다. csrf 공격은 여전히 가능하겠지만, 스크립트 삽입 공격(xss)은 막히게 된다(csrf 는 보안에 조금만 신경써도 쉽게 차단가능하다, 프론트에서 자주쓰는 axios, ajax 라이브러리 경우 자체적으로 지원해주는 기능이기도 하고).
결론
- Refresh Token은 보안을 위해 httpOnly, http secure(https로만 통신) 옵션을 사용해서 쿠키에 보관하는 것이 좋다
- Access token은 메모리에 보관한다. (쿠키는 4KB size limit이 있는데 JWT는 보관하지 못하며, 로컬 스토리지는 가장 취약하다)
로컬 스토리지- 브라우저에 영구적으로 저장되는 값. 키와 벨류형태로 저장한다. (비회원으로 장바구니 담을 때 사용)
'Web > 보안' 카테고리의 다른 글
비밀번호 암호화 함수로 Bcrypt를 사용하는 이유 (0) | 2023.03.10 |
---|