일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 이친수
- react
- 플로이드 #c++
- raect typescript #react #typescript #styled-component
- useState #Hooks
- React #리액트 이벤트 주기 #리액트 이벤트
- 버블링 #갭쳐링 #이벤트 #JS
- 빡킹독
- DP #c++
- 코드스테이츠 #알고리즘 #그리디
- 백준 #직각삼각형
- React-Query
- react fragment
- JWT #토큰 #refreshToken #accessToken #Token #token #localStorage #sessionStorage
- axios
- donwstream #upstream #origin
- npm #not being able to find a file #npm install Error
- RateLimit
- 다익스트라 #파티 #백준
- rate limit
- 노마드 코더 #타입스크립트 #typescript #class
- 얕은 복사 #깊은 복사 #shallow copy #deep copy
- html entities
- React #Hook rules #Hook 규칙
- react #useCallback #react Hook
- React #controlled component #비제어 컴포넌트 #제어 컴포넌트
- React #effect hook #useEffect
- 백준 #적록색약
- #useRef #언제 쓰는데?
- interceptors
- Today
- Total
꿈꾸는 개발자
JWT 토큰 저장 방식?/위치? 정리 (보안) 본문
배경 지식(토큰 인증 방식)
토큰 인증 방식은 사용자의 인증 정보를 서버 측이 아닌 클라이언트 측에 저장하는 방식이다. (세션 인증 방식이 가지고 있던 한계를 극복할 수 있음- 서버에서 관리하던 인증 정보에 대한 부담 완화).
기본 흐름
JWT(Json Web Token)
토큰 기반 인증을 사용했을 때 대표적으로 사용하는 방식 중 하나가 JWT이다. JWT는 JSON 객체에 정보를 담아 이를 토큰으로 암호화하여 전송할 수 있는 기술을 의미한다. 클라이언트에선 서버에 요청을 보낼 때 JWT 토큰을 제공하면 서버에서 해당 토큰을 검증 후 유저 정보를 확인할 수 있다.
기본 구조(base64로 인코딩 된다)
- header: 토큰 자체를 설명하는 데이터를 담고 있다.(암호화하는 방식): base64방식으로 인코딩한다. 얼마든지 디코딩될 수 있기 때문에 민간한 정보는 담지 않아야 한다.
- payload: HTTP payload처럼 전달하고자 하는 내용을 담고 있다. 어떤 정보에 접근이 가능한지에 대한 권한, 유저 개인정보, 토큰 발급 시간 등 JSON형태 => base64로 인코딩되면 위 payload 완성
- signature: 토큰의 무결성을 확인할 수 있는 부분, header+payload가 완성됐다면, signature는 서버의 비밀 키를 통해 해싱을 할 수 있다
위험성 및 한계
JWT를 탈취하기 위해서 사용할 수 있는 방법은 "XSS 공격"과 "CSRF 공격"이 있다. 만약 토큰이 탈취된다면, 개인 정보를 조회하거나, 접근성이 허용되지 않은 정보에 접근할 수 있게 됨으로 보안의 중요도는 매우 높다.
토큰 방식의 한계
토큰 인증 방식에도 분명한 한계가 존재한다.
signature를 통해 위조 토큰을 분별할 수 있지만, 토큰 자체가 탈취됐을 경우 토큰 인증 방식의 한계가 분명해진다.
- 무상태성: 인증 상태를 관리하는 게 서버가 아니기 때문에 토큰을 강제로 만료시킬 수 없다. 따라서 만료될 때까지 사용자인 척 계속 요청을 보낼 수 있음
- 유효기간: 토큰 만료 기간을 길게 설정하면 탈취 될 위험성이 증가하고, 짧게 설정하면 사용자 경험에 안 좋은 영향을 미친다
한계 보완
토큰 인증 방식의 한계를 극복하기 위해 고안된 방법 중 하나로 accessToken과 refreshToken 방식이 있다.
- accessToken: 서버에 접근하기 위해 axios 등 authorization header에 넣어주는 토큰이다. 위에서 언급한 토큰과 비슷한 역할을 담당한다. (보통 만료 기간을 24시간으로 설정한다)
- refreshToken: 서버에 접근을 위해 존재하는 토큰이 아닌, 엑세스 토큰이 만료됐을 경우 accessToken을 재발급 받기 위해 사용되는 토큰이라고 생각하면 된다. 따라서, RefreshToken(만료기간) > AccessToken(만료기간)으로 보통 설정된다.
물론 refresh 토큰 마저 탈취된다면 큰 문제가 발생하겠지만, 이러한 상황을 방지하기 위해서 refresh 토큰은 보통 서버에 관리를 한다.
저장 위치
그렇다면 클라이언트 측에서 사용자가 로그인 후 서버로부터 토큰을 받게 됐을 때 이것을 어디에 저장하고 사용하는 것이 바람직한 방법일까? 토큰을 저장할 수 있는 방식 중 대표적으로 비공개 변수, 로컬 스토리지, 세션 스토리지, 쿠키로 분류를 할 수 있다. (그냥 일반적인 토큰 방식이든 accessToken,RefreshToken 방식이든) 토큰 방식 자체가 클라이언트에서 토큰을 저장해야 하기 때문에 위의 고민은 필수적이다.
- 비공개 변수의 경우 브라우저가 새로고침될 때마다 유지가 되지 않기 때문에 사용자는 새로고침 할 때마다 재접속을 해야하는 불편함을 감수해야 한다. 따라서, 그렇게 적극적으로 사용되는 방법은 아닌듯 하다. (하지만 refreshToken을 통해 재접속하는 API를 작성하면 되다는 글을 보긴 했는데 정확도 의심되기 때문에 참고 사항 정도로 일단 보류하고자 한다)
로컬 스토리지+ 세션 스토리지(공통)
- 쿠키 방식과 다른 점:
- 쿠키와 다르게 네트워크 요청 시 서버로 전송되지 않는다. 쿠키보다 더 많은 자료를 보관할 수 있다
- 서버가 HTTP 헤더를 통해 스토리지 객체를 조작할 수 없음 웹 스토리지 객체 조작은 모두 JS로(이것은 추후 보안에 문제가 된다)
- 웹 스토리지 객체는 도메인/프로토콜/포트로 정의되는 origin에 묶여있다. 다른 도메인에서 접근 시 CORS 에러가 발생하게 됨
- 로컬 스토리지: 많이들 사용하는 방식인 것 같다. 나도 이 방법을 위주로만 사용했었던 것 같은데. 일단 브라우저가 새로고침되더라도 정보들이 유지되기 때문에 사용자 편의성이 높은 편이다(브라우저를 닫고 다시 열어도 정보들이 유지된다). 하지만, JS 코드를 통해 접근이 가능하기 때문에 XSS 공격에는 취약하고 CSRF 공격에는 안전한다.
- 세션 스토리지: 로컬 스토리지에 비해 제한적이다. 현재 떠 있는 텝에서만 유지되는 특징을 가진다. 새로 고침할 때는 사라지지 않지만, 탭을 닫고 다시 열 때는 데이터가 사라진다.
쿠키
MDN 공식 문서에 따르면 쿠키에 저장하는 방식보단 로컬 스토리지에 저장하는 형식을 조금 더 권장하는 듯하다.
- 그 이유는 모든 요청에 쿠키가 함께 전송되기 때문에 성능 저하의 원인될 수 있다고 합니다.
보안적인 측면에서 쿠키 또한 JS로 접근이 가능하기 때문에 서버측에서 HTTP Only,secure,Samesite 등 옵션을 걸어줘여 합니다. 위와 같은 방법으로 쿠키 탈취/JS 접근도 제한할 수 있습니다.
결론
본 글은 팀 프로젝트를 진행하면서 제기된 토큰 저장 방식에 대한 고찰을 정리하고자 작성됐다. 아직 어떠한 형식으로 토큰을 저장할지 완벽하게 결정하지 않았지만, 공식문서 등 여러 상황들을 종합적으로 고려해 결정하고자 한다. 결론 내용은 프로젝트를 진행하면서 추가적으로 보충하고자 한다
참고 자료
https://oauth.net/2/refresh-tokens/
https://ko.javascript.info/localstorage
추가적으로 참고하면 좋을 것 같은 글 (비공개 변수에 Token을 저장하는 방식에 대해 다루고 있음)