본문 바로가기
웹 프로그래밍/👨‍👨‍👧‍👧소셜 가계부

[React/Node.js/Express/MongoDB] 소셜 가계부 프로젝트 구현 일지: 개인정보, 토큰 저장 방식 변경2 (redux-persist 보안: redux-persist-transform-encrypt)

by 청량리 물냉면 2024. 3. 31.
반응형

ctyptoJS

 

crypto-js

JavaScript library of crypto standards.. Latest version: 4.2.0, last published: 5 months ago. Start using crypto-js in your project by running `npm i crypto-js`. There are 11736 other projects in the npm registry using crypto-js.

www.npmjs.com

`ctyptoJS`는 암호화 표준을 제공하는 자바스크립트 라이브러리이다.

원래는 `redux-persist`를 이용해 저장한 Local Storage 데이터를 암호화하기 위해 crptoJS 라이브러리를 사용해 보려고 했다. 그런데 라이브러리 설명 페이지에 접속해 보니 위 라이브러리는 단종(Discontinued)되었다고 한다.

추가 개발이 이루어지지 않고 더 이상 유지관리 되지도 않는다고 해서, 암호화에는 다른 방법을 사용해 보기로 했다.

단순 Local Storage 암호화 방식으로는 ` React Secure Storage` 설치와 같은 방법이 있는 것 같다. (참고 블로그)

하지만 나는 `redux-persist`로 저장한 데이터를 암호화하려는 것이므로 `redux-persist`를 키워드로 잡고 보안에 대해 검색해 보았다. 그 결과 `redux-persist-transform-encrypt` 라는 라이브러리를 알게 되었다.

 

 

redux-persist-transform-encrypt

 

redux-persist-transform-encrypt

Encrypt your Redux store.. Latest version: 5.1.1, last published: 4 months ago. Start using redux-persist-transform-encrypt in your project by running `npm i redux-persist-transform-encrypt`. There are 40 other projects in the npm registry using redux-pers

www.npmjs.com

redux-persisit로 저장한 storage를 암호화시켜주는 라이브러리이다.

설명 페이지의 코드를 참고해 가며 내 프로젝트의 `rootReducer`를 수정해 주었다.

 

기존 코드

//rootReducer.js

import { combineReducers } from "redux";
import { persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";

import userReducer from "./user";
import transactionReducer from "./transactions";
import transactionAnalyticsReducer from "./transactionAnalytics";

const persistConfig = {
  key: "root",
  whitelist: ["user"],
  storage,
};

const rootReducer = combineReducers({
  user: userReducer,
  transactions: transactionReducer,
  transactionAnalytics: transactionAnalyticsReducer,
});

export default persistReducer(persistConfig, rootReducer);

👇

수정한 코드

//rootReducer.js

import { combineReducers } from "redux";
import { persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";

import userReducer from "./user";
import transactionReducer from "./transactions";
import transactionAnalyticsReducer from "./transactionAnalytics";
import { encryptTransform } from "redux-persist-transform-encrypt"; //추가

const persistConfig = {
  key: "root",
  whitelist: ["user"],
  storage,
};

const rootReducer = combineReducers({
  user: userReducer,
  transactions: transactionReducer,
  transactionAnalytics: transactionAnalyticsReducer,
});

//추가
const reducer = persistReducer(
  {
    ...persistConfig,
    transforms: [
      encryptTransform({
        secretKey: "my-super-secret-key",
      }),
    ],
  },
  rootReducer,
);

export default reducer;

암호화된 로컬스토리지 데이터

 

 

redux-persist-transform-encrypt의 secret ket??

그런데 npmjs 사이트의 라이브러리 설명을 읽어보니, 모든 사람에게 동일한 키를 사용하지 말라고 되어 있다.

그럼 암호를 유저마다 다르게 생성하라는 뜻인 거 같은데...

아래를 문장을 보니 사용자의 access 토큰이나 세션 키를 비밀 키로 사용하라고 되어 있다.

 

그런데 나의 경우 store의 user 항목에 사용자의 토큰값과 유효기간이 저장되어 있다.

`root Reducer.js`에서 비밀 키를 설정해야 되는데, `root Reducer` 아래에 있는 `userReducer`를 이용해 토큰을 가져와서 사용하는 게 가능한 일인가?🙄

심지어 암호화를 진행하려는 이유도 user 항목의 정보들을 숨기기 위해서인데, 거기에 있는 내용을 꺼내와서 비밀 키로 쓴다니 내 프로젝트 파일 구조로는 아무리 생각해도 적법한 방법이 아닌 것 같았다.  

관련한 내용을 찾아보려 했지만 인터넷 상에 이 라이브러리에 대한 정보가 생각보다 많이 없다. 

어쩔 수 없이 키를 환경 변수에 따로 저장하는 방식으로 보안을 유지하기로 했다.

 

//.env

REACT_APP_PERSISTOR_SECRET_KEY=super secret key
//rootReducer.js

const reducer = persistReducer(
  {
    ...persistConfig,
    transforms: [
      encryptTransform({
        secretKey: process.env.REACT_APP_PERSISTOR_SECRET_KEY, //환경 변수 사용
      }),
    ],
  },
  rootReducer,
);

 

vercel의 배포된 어플 관리 페이지에 들어가서 환경 변수도 새로 추가해 주었다.

배포까지 잘 완료되었다.

 

https://social-account-book-frontend.vercel.app/

 

소셜 가계부

 

social-account-book-frontend.vercel.app

 

 

후기

처음에 토큰 정보를 Local Storage에 저장한 이유는 프로젝트 진행하며 참고했던 강의에서 그렇게 데이터를 저장했기 때문이었다. 유저 정보가 상당히 중요하게 다루어지는 데이터라는 걸 알고 있었지만, 기능 구현에 급급했기에 보안에 대한 신경을 쓰지 못했고, 그래서 데이터를 Local Storage에 저장할 때의 장단점 같은 건 추후 생각하기로 하고 미뤄두었었다.

 

그러다 얼마 전 멘토링 진행 중 멘토께서 왜 유저 데이터를 Local Storage에 저장했냐고, 사용자의 데이터를 Local Storage에 저장하면 큰일 난다! 는 말씀을 해주셨다. 현업에서는 대신 Session, Cookie 등을 사용한다고 한다. 물론 실무 경력이 많지 않은 주니어 분이었지만 실제 현업에서 그렇게 쓰인다니 내가 보안적인 면에 있어서는 실수를 저질렀구나 하는 생각을 했다. 

 

그래서 이번 기회에 Local Storage에 저장해 사용하고 있던 유저 정보를 다른 방식으로 저장해 보기로 했다. 

구현에 앞서 가장 먼저 공부해야 할 부분은 Cookie, Web Storage (Session Storage,  Local Storage)의 공통점과 차이점에 대한 분석이었다.

 

우선 Cookie, Web Storage의 공통점은 둘 모두 해당 도메인에 대한 데이터를 브라우저에 저장하는 저장소라는 것이다. 

 

 

Cookie

  • 웹 서버가 생성하여 웹 브라우저로 전송하는 작은 정보 파일
  • 일반적으로 웹사이트 접속시 사용자의 컴퓨터에 다운로드된다
  • 이름, 값, 도메인 정보, 경로 정보, 만료 일자 및 시간 등의 데이터를 저장한다.
  • 단점 1: 웹 사이트에서 쿠키를 설정하면 이후 모든 웹 요청은 쿠키정보를 포함하여 서버로 전송된다.
  • 단점 2: 저장 용량이 작다. (최대 4KB)
  • 단점 3: 쿠키는 사용자의 로컬 컴퓨터에 파일 형태로 저장되며, 따라서 클라이언트에서 쉽게 탈취/변경될 수 있어 보안에 취약하다.
    • ex) 자바스크립트를 통해 조회가 가능하기 때문에, XSS공격에 취약하다.→ HttpOnly 옵션으로 자바스크립트를 이용한 접근을 차단할 수 있다.
  • 다시 보지 않기 팝업 창 구현에 이용

 

Web Storage (Session Storage,  Local Storage)

  • HTML5부터 제공하는 기능
  • 쿠키의 단점을 보완해 등장한 웹 스토리지를 사용한다.
  • 쿠키와 기능은 유사하지만, 클라이언트에 저장만 할 뿐 서버로 전송되지 않는다.
  • 지속성에 따라 Session Storage, Local Storage 로 구분한다.
  • Local Storage: 브라우저에 반영구적으로 데이터 저장. 브라우저 종료 시에도 데이터가 유지됨 → 자동 로그인 기능 구현에 이용
  • Session Storage: 탭 윈도우 단위로 생성됨. 탭을 닫으면 데이터가 삭제됨 → 입력 폼 정보, 비로그인 장바구니 기능 구현에 이용

 

세션의 장점은 Session 기간 동안만 데이터를 저장하기 때문에 보안 측면에서 유리하다는 것이다.

나의 경우 Local Storage를 사용하기는 했지만, 토큰에 만료기간을 정해 3시간 이후로는 자동 로그아웃 시키고 Local Storage 데이터를 모두 삭제하도록 하며 로컬 스토리지가 지닌 단점(데이터가 로컬에 계속 남아있어 보안에 취약할 수 있다는 단점)을 보완하려 했다. 

하지만 그럼에도 멘토님께 지적받은 대로 유저 데이터가 브라우저에 그대로 노출되고 있었기에, 해당 부분을 로컬 스토리지 데이터를 암호화함으로써 개선해 보았다. 

 

토이 프로젝트라는 이유로 보안에 관해서는 소홀하게 생각한 면이 있는데 프로젝트를 개선하기 위해 공부하는 과정에서 다양한 해킹 공격이 실제로 발생하고 있고, 그로 인해 개발자는 항상 개발하는 프로그램의 보안에 대해 경각심을 가져야 한다는 사실도 다시 한번 되새기게 되는 기회였다.

덕분에 `redux-persist`와 `redux-persist-transform-encrypt` 라이브러리를 알게 되었고, 리덕스의 정보를 로컬 스토리지에 담아 브라우저에 저장하는 방법과 그렇게 저장된 데이터를 암호화하는 방법까지 익힐 수 있었다. 

또한 이름만 들어보았던 session, cookie, local storage의 개념에 대해서도 깊이 있게 공부할 수 있어 굉장히 유익한 과정이었다고 생각한다. 

이번에 배웠던 개념은 꼭 잘 기억해 두었다가 다음 프로젝트에 적용해 보아야겠다. 

 

 

로그인 처리하는 방법 중에 Local Storage를 사용하지 않고도 로그인하는 방법이 있길래 가져와 봤다... 나중에 이 방법으로도 시도해 봐야겠다.

 

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

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

velog.io

 

 


공부에 참고한 자료

 

 

쿠키와 웹 스토리지, 그리고 보안

로그인 기능을 구현하던 도중 사용자 인가 정보 저장에 대한 필요성이 생겼다. 그에 따라 브라우저가 제공하는 기능에는 뭐가 있고 해당 기능들의 장단점에 대해 알아보았다. 로그인 기능을 구

velog.io

 

JWT는 어디에 저장해야할까? - localStorage vs cookie

이번에 지하철 미션을 만들면서 JWT를 클래스 property에 저장했었는데 리뷰어 분께 해당 부분을 피드백 받으면서 어디에 JWT를 저장하는 것이 좋을까 에 대해 고민해보게 되었다. 0. 기본 지식 JWT Js

velog.io

 

Cookie vs LocalStorage vs SessionStorage : 차이점은 무엇일까?

목차 LIST Cookie 최대 4KB 용량을 가진 매우 작은 양의 데이터로 방문한 페이지를 저장하거나 유저의 로그인 정보를 저장하는 것과 같이 다양한 방법으로 사용되며, 문자열만 저장 가능하다는 제한

sunrise-min.tistory.com

 

쿠키(CooKie)에 대하여

“쿠키(CooKie)”란 유저들의 효율적이고 안전한 웹사이트 사용을 보장하기 위하여 사용되는 작은 정보기록 파일로써, 일반적으로 웹사이트 접속시 사용자의 컴퓨터에 다운로드됩니다. 웹사이트

www.coinex.com

 

[web] LocalStorage, SessionStorage, Cookie의 차이점

WEB STORAGE HTML5 에는 웹의 데이터를 클라이언트에 저장할 수 있는 새로운 자료구조인 Web Storage 스펙이 포함되어 있다. Web Storage의 개념은 키/값 쌍으로 데이터를 저장하고 키를 기반으로 데이터를

velog.io

 

[JS] 📚 LocalStorage / SessionStorage (vs 쿠키와 비교)

LocalStorage / SessionStorage API 소개 html5에서는 좀 더 쉽고 간단한 저장소 제공을 위해 새로운 localStorage와 sessionStorage API를 제공한다. 둘 다 저장 공간으로 사용할 수 있는데 이 둘의 가장 큰 차이점이

inpa.tistory.com

 

localStorage와 sessionStorage

 

ko.javascript.info

 

쿠키에 보안 정보를 저장해도 되는가?

쿠키에는 지워져도 되고, 조작되거나 가로채이더라도 큰 지장이 없는 수준의 정보들만을 보관하도록 되어 있다. 즉, 쿠키에는 보안정보를 저장하면 안된다. 쿠키는 어떻게 정보를 유출할 수 있

velog.io

 

쿠키가 세션보다 보안에 취약한 이유가 뭔가요? - 인프런

쿠키가 세션보다 보안에 취약한 이유가 잘 이해가 가지 않습니다1. 쿠키가 세션보다 더 털릴 가능성이 높아서인가요?근데 세션도 결국 쿠키의 형태로 로컬에 저장되잖아요!2. 그리고 chatgpt에게

www.inflearn.com

 

반응형