프로젝트 소중해를 진행시켜 나가면서, 이제 인증을 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 정보 잃지 않도록 수정
- 이젠 Redux에 리로드를 하여도 저장되어 있는 걸 볼 수 있다!
**단점**
하지만 검색해보니 이 방법은 XSS에 취약하다는 단점이 있었는데...
벨로퍼트님의 글 하단에서 볼 수 있듯,
서버가 토큰을 발급해주면, 브라우저에서 사용자 / 서버간에 토큰이 전달되는 방식은 크게 두가지로 나뉩니다. 첫번째는, 로그인에 성공하게 되었을 때 서버가 토큰을 응답정보에 토큰을 넣어서 전달하도록 하고, 해당 값을 웹 스토리지 (localStorage 혹은 sessionStorage) 에 넣고, 그 다음부터 웹 요청을 할 때마다 HTTP 헤더 값에 넣어서 요청하는 방법이 있습니다.
이 방법은, 구현하기 쉽고, 하나의 도메인에 제한되어있지 않다는 장점이 있지만, XSS 해킹 공격을 통하여 해커의 악성스크립트에 노출이 되는 경우 매우 쉽게 토큰이 탈취 될 수 있습니다. 그냥 localStorage 에 접근하면 바로 토큰에 접근 할 수 있기 때문이죠.
이에 대한 대안, 두번째 방식은 이 토큰을 쿠키에 넣는 것 입니다. 쿠키를 쓰지 않으려고 토큰을 쓰는 줄 알았는데, 갑자기 이렇게 설명을 하니 헷갈릴 수도 있습니다. 쿠키를 사용한다고해서 세션을 관리하는것은 아니고, 그저 쿠키를 정보 전송수단으로 사용 할 뿐입니다. 이 과정에서, 서버측에서 응답을 하면서 쿠키를 설정 해 줄 때 httpOnly 값을 활성화를 해주면, 네트워크 통신 상에서만 해당 쿠키가 붙게 됩니다. 따라서, 브라우저상에서는, 자바스크립트로 토큰값에 접근하는것이 불가능해지죠.
두번째 대안에 대한 단점은, 쿠키가 한정된 도메인에서만 사용이 된다는 점 입니다. 이 부분은, 토큰이 필요해질 때 현재 쿠키에 있는 토큰을 사용하여 새 토큰을 문자열로 받아올 수 있게 하는 API를 구현하여 해결하면 됩니다.
또 다른 단점은, XSS의 위험에서 완벽히 해방되는 대신, CSRF 공격의 위험성이 생긴다는 점 입니다. CSRF의 경우엔 HTTP 요청 레퍼러 체크, 그리고 CSRF 토큰의 사용을 통하여 방지 할 수 있습니다.
와 같이 본 글에서 설명한 local storage에 토큰 정보를 저장하는 것은 보안상 취약하다.
+ 두 가지 방법의 보안 상 장단점,
그리고 그것을 바탕으로 로그인 구현을 어떻게 하면 좋을 지
아래의 블로그에 잘 나와 있다.
* JWT로 유저 인증
* refreshToken을 secure httpOnly 쿠키로, accessToken은 JSON payload로 받아와서 웹 어플리케이션 내 로컬 변수로 이용
* 이를 통해 CSRF 취약점 공격 방어하고, XSS 취약점 공격으로 저장된 유저 정보 읽기는 막을 수 있음
* 하지만 XSS 취약점을 통해 API 콜을 보낼 때는 무방비하니 XSS 자체를 막기 위해 서버와 클라이언트 모두 노력해야 함
- yaytomato님velog
와 같이 구현하는 것도 고려해 보잣!
'개발 관련 개념들' 카테고리의 다른 글
CSS가 이상하게 적용될 때, user agent stylesheet체크!! (0) | 2021.04.24 |
---|---|
IntelliJ에서 Gradle기반 스프링 프로젝트 실행 - 오류 해결 로그 (0) | 2021.04.20 |
npm run build시 html-webpack-plugin 에러 (0) | 2021.04.14 |
git 충돌 시 해결법 (0) | 2021.04.06 |
윈도우즈 (Windows 10) 에서 우분투 (Ubuntu) 설치하기 (0) | 2020.11.18 |
댓글