본문 바로가기
개발 관련 개념들

[JWT] 클라이언트 쪽에서 JWT 사용 - React / Redux

by 코곰 2021. 4. 20.

프로젝트 소중해를 진행시켜 나가면서, 이제 인증을 JWT를 사용해 할 시간이 되었다.

HS님께서 전달해주신 React/Redux 기반의 프론트엔드 시스템에서, 어떻게 JWT를 사용하는 지 보여주는 유툽 비디오 내용을 기록하려 한다.

 

출처 - www.youtube.com/watch?v=FyyPUIAe6kc

 

 

내용 정리

1. JWT를 서버에서 받고 로컬에 저장하기

- /api/auth에 로그인 데이터를 POST 해주면

JWT 정보를 가진 프로미스가 반환된다는 전제 하에

다음과 같은 action 생성

- LogInForm에서는 위의 액션을 추출해 사용

- 이후 로그인을 하고, 로그인 정보가 맞다면,

우리가 deconstruct한 token이

'jwtToken'이라는 key로 local storage에 저장된 것을 볼 수 있다.

 

 

2. jwtToken을 HTTP Request를 보낼 때 헤더에 포함시키기

- 헤더에 포함시키기 위해 새로운 함수를 만들자

 

- utils/setAuthorizationToken.js

 

- 이렇게 토큰이 있으면 header에 포함시키고, 없으면 그 부분을 지우는 함수.

NOTE. access token은
(1) query string
(2) HTTP request header - "Authorization: Bearer "형식
에 포함시키는 두 가지 방법이 있지만, query string은 서버 로그에 기록이 남을 수 있어 보안상의 이유로 잘 쓰지 않는다. (출처 - 생활코딩 OAuth)

- 그러면 이제, 로그인 성공 시 Header에 Token 정보를 저장할 수 있도록

아까 만든 함수에 setAuthorizationToken 부분을 삽입해주자.

- 그리고 reload시에도 저장될 수 있도록, index.js파일 자체에도 함수를 추가.

- 그러면 이제, Application tab에서 jwtToken을 지우지 않는 이상, HEADER에 계속 Authorization부분이 있는 것을 볼 수 있다.

 

3. JWT이용하기 쉽게 decode하기

- decode해서 저장해주기 위해 아래와 같은 코드 추가.

 

- 하지만 실행하면 다음과 같은 에러가 뜬다.

- JWT가 필요로 하는 'net'이나 'dns'모듈이 없기 때문.

 

- webpack.config.dev.js에서 그 부분을 추가해준다.

 

- 이후 다시 로그인하면 console에 decode된 JWT가 잘 찍히는 걸 볼 수 있음.

 

 

4. Redux에 저장

- 중앙저장소 Redux에 JWT를 통해 얻은 정보를 저장해 사용한다면 편리할 것.

- authReducer를 만들어 다음과 같이 씀

- 만든 authReducer는 중앙 Reducer에 합체!

- 그리고 그 액션을 dispatch할 함수를 만들고 사용!

- 이러면 리덕스에 정보가 잘 저장되는 걸 볼 수 있다.

 

5. 리로드때도 Redux 정보 잃지 않도록 수정

lines 9, 10, 12-15 Added

 

- 이젠 Redux에 리로드를 하여도 저장되어 있는 걸 볼 수 있다!

 

 

 

**단점**

하지만 검색해보니 이 방법은 XSS에 취약하다는 단점이 있었는데...

backend-intro.vlpt.us/4/

 

4장. JWT 이해 및 적용 · GitBook

4장. JWT 이해 및 적용 사용자의 인증정보를 관리하기 위해서, 우리는 토큰 기반 인증 시스템을 사용 할 것 입니다. 사용자의 인증정보를 관리하는 방식은 크게 두가지로 나뉘는데요, 첫째는 세션

backend-intro.vlpt.us

벨로퍼트님의 글 하단에서 볼 수 있듯,

서버가 토큰을 발급해주면, 브라우저에서 사용자 / 서버간에 토큰이 전달되는 방식은 크게 두가지로 나뉩니다. 첫번째는, 로그인에 성공하게 되었을 때 서버가 토큰을 응답정보에 토큰을 넣어서 전달하도록 하고, 해당 값을 웹 스토리지 (localStorage 혹은 sessionStorage) 에 넣고, 그 다음부터 웹 요청을 할 때마다 HTTP 헤더 값에 넣어서 요청하는 방법이 있습니다.

이 방법은, 구현하기 쉽고, 하나의 도메인에 제한되어있지 않다는 장점이 있지만, XSS 해킹 공격을 통하여 해커의 악성스크립트에 노출이 되는 경우 매우 쉽게 토큰이 탈취 될 수 있습니다. 그냥 localStorage 에 접근하면 바로 토큰에 접근 할 수 있기 때문이죠.

이에 대한 대안, 두번째 방식은 이 토큰을 쿠키에 넣는 것 입니다. 쿠키를 쓰지 않으려고 토큰을 쓰는 줄 알았는데, 갑자기 이렇게 설명을 하니 헷갈릴 수도 있습니다. 쿠키를 사용한다고해서 세션을 관리하는것은 아니고, 그저 쿠키를 정보 전송수단으로 사용 할 뿐입니다. 이 과정에서, 서버측에서 응답을 하면서 쿠키를 설정 해 줄 때 httpOnly 값을 활성화를 해주면, 네트워크 통신 상에서만 해당 쿠키가 붙게 됩니다. 따라서, 브라우저상에서는, 자바스크립트로 토큰값에 접근하는것이 불가능해지죠.

두번째 대안에 대한 단점은, 쿠키가 한정된 도메인에서만 사용이 된다는 점 입니다. 이 부분은, 토큰이 필요해질 때 현재 쿠키에 있는 토큰을 사용하여 새 토큰을 문자열로 받아올 수 있게 하는 API를 구현하여 해결하면 됩니다.
또 다른 단점은, XSS의 위험에서 완벽히 해방되는 대신, CSRF 공격의 위험성이 생긴다는 점 입니다. CSRF의 경우엔 HTTP 요청 레퍼러 체크, 그리고 CSRF 토큰의 사용을 통하여 방지 할 수 있습니다.

와 같이 본 글에서 설명한 local storage에 토큰 정보를 저장하는 것은 보안상 취약하다.

 

 

+ 두 가지 방법의 보안 상 장단점,

그리고 그것을 바탕으로 로그인 구현을 어떻게 하면 좋을 지

아래의 블로그에 잘 나와 있다.

velog.io/@yaytomato/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%90%EC%84%9C-%EC%95%88%EC%A0%84%ED%95%98%EA%B2%8C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0

 

🍪 프론트에서 안전하게 로그인 처리하기 (ft. React)

localStorage냐 쿠키냐 그것이 문제로다

velog.io

* JWT로 유저 인증
* refreshToken을 secure httpOnly 쿠키로, accessToken은 JSON payload로 받아와서 웹 어플리케이션 내 로컬 변수로 이용
* 이를 통해 CSRF 취약점 공격 방어하고, XSS 취약점 공격으로 저장된 유저 정보 읽기는 막을 수 있음
* 하지만 XSS 취약점을 통해 API 콜을 보낼 때는 무방비하니 XSS 자체를 막기 위해 서버와 클라이언트 모두 노력해야 함

- yaytomato님velog

와 같이 구현하는 것도 고려해 보잣!

댓글