민감한 개인정보를 그대로 데이터베이스에 저장하여 클라이언트, 서버랑 주고받다보면 해커로부터의 정보탈취에 노출될 수 있다. 해커의 공격을 받아도 비밀번호 같은 민감한 정보를 암호화하여 저장하면 정보가 탈취되더라도 암호화를 풀지 않는 이상 정보를 알아낼 수 없다.
정보를 암호화하는 것을 hashing이라고 하는데, 해싱에 이용하는 것이 바로 hash함수이다. 암호화하는 함수를 이용하여 비밀번호를 암호화하는 것이다.
해시함수중에는 여러가지가 있는데, 그 중에서 bcrypt를 사용해서 비밀번호를 암호화해줬다. bcrypt의 특징은 아래와 같다.
즉 강력한 보안이 필요한 곳에 사용하는 해시함수이다. 또한 비교적 구현이 쉬워서 처음하는 프로젝트의 비밀번호 암호화에서 사용해봄직 하다고 생각했다.
우선 npm install bcrypt —save로 비크립트를 설치해준다.
const bcrypt = require('bcrypt');
module.exports = {
hashPassword: async (plainPassword) => {
const password = plainPassword;
const saltRounds = 12;
const hashedPassword = await new Promise((resolve, reject) => {
bcrypt.genSalt(saltRounds, (err, salt) => {
bcrypt.hash(password, salt, (err, hash) => {
if (err) reject(err);
resolve(hash);
});
});
});
return hashedPassword;
},
};
유저가 입력한 패스워드를 받와와서 암호화된 패스워드로 바꿔주는 함수를 작성해준다.
여기서 salt란 암호화를 할 때 넣는 랜덤한 글자를 말한다. salt를 넣어서 암호화를 해주는 이유는, 공격자가 암호를 유추할 수 없도록, 평문에 의미없는 데이터를 뿌려서 해독을 어렵게 만들기 위함이다.
bcrypt에서는 genSalt()메소드를 이용해 salt를 생성해준다. saltaround는 Salt를 적용하여 나온 결과 해시에다가, 다시 동일한 Salt을 적용하여 한번 더 결과 해시를 도출하고, 또 다시 반복시키는 횟수이다. 위의 로직이 12번 실행되는 셈이다.
위에서 나온 hashedPassword를 DB에 저장해주면 된다.
회원가입과 로그인을 완성하고, 로그인이되면 isLogin의 상태를 바꿔주었는데, isLogin상태가 계속 유지되지 않는다는 문제가 있었다. redux의 store는 무상태성으로 페이지를 벗어나게되면 유지가 되지 않는다. 이렇게 무상태성을 보안하기 위해, 상태를 local storage나 session storage에 저장하는 방법이 있는데, 이때 쓰는 것이 redux-persist이다. 기존 store를 persist store로 바꿔주면 된다.
index.js에서 넣어준 store밑에 persistGate를 넣어준다
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import GlobalStyle from './styles/globalstyles';
import { BrowserRouter } from 'react-router-dom';
import { ThemeProvider } from 'styled-components';
import { theme } from './styles/theme';
import store from './redux/store';
import { Provider } from 'react-redux';
import { persistStore } from 'redux-persist';
import { PersistGate } from 'redux-persist/integration/react';
import './fonts/font.css';
const persistor = persistStore(store);
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<BrowserRouter>
<ThemeProvider theme={theme}>
<GlobalStyle />
<App />
</ThemeProvider>
</BrowserRouter>
</PersistGate>
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
이후 redux-persist에서 persistReducer와, storage를 불러와서 아래와같이 작성해주면 된다.
import { combineReducers } from 'redux';
import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import loginReducer from '../redux/login/reducer';
import { adminReducer, adminOpenReducer } from '../redux/admin/reducer';
import { openModalReducer } from '../redux/signup/reducer';
import { guideCardsReducer, guideModalReducer } from '../redux/map/reducer';
import { cardFilterReducer } from '../redux/mapFilter/reducer';
import scrollReducer from '../redux/scroll/reducer';
import toggleReducer from '../redux/toggle/reducer';
import { chatUserInfoReducer, chatListReducer, currentRoomReducer } from '../redux/chat/reducer';
import { guideDeleteReducer } from './management/reducer';
import {
openTourModalReducer,
openDeleteModalReducer,
completeDeleteReducer,
} from './tourManagement/reducer';
const persistConfig = {
key: 'root',
storage,
whiteList: ['loginReducer', 'adminReducer'],
blacklist: [
'cardFilterReducer',
'guideModalReducer',
'guideCardsReducer',
'chatUserInfoReducer',
'chatListReducer',
'currentRoomReducer',
'guideDeleteReducer',
'openTourModalReducer',
'openDeleteModalReducer',
'completeDeleteReducer',
],
};
const rootReducer = combineReducers({
loginReducer,
adminReducer,
adminOpenReducer,
scrollReducer,
toggleReducer,
openModalReducer,
cardFilterReducer,
guideCardsReducer,
guideModalReducer,
chatUserInfoReducer,
chatListReducer,
currentRoomReducer,
guideDeleteReducer,
openTourModalReducer,
openDeleteModalReducer,
completeDeleteReducer,
});
export default persistReducer(persistConfig, rootReducer);
persistConfig에는 key와 어디에 저장할 것인지의 storage, storage에 저장할 목록인 whiteList, 제외할 목록인 blackList로 나눠서 구성해주면 된다. 그 다음
export default persistReducer(persistConfig, rootReducer);
로 export해주면 된다.

localstorage에 잘 들어가있는 걸 확인할 수 있다.