[Node.js] Express + MongoDB part#3 데이터 보안

Duboo·2022년 9월 28일
0

Server + DB

목록 보기
3/6
post-thumbnail

MongoDB 사용법 참고


클라이언트와 서버 어느쪽이든 개발을 진행할 때 노출되어선 안되는 데이터가 있기에 이러한 민감한 데이터를 관리하는 방법

config

Index.js

const mongoose = require("mongoose");

mongoose
  .connect(
    "복사한 url과 id/password"
  )
  .then(() => console.log("MongoDB 연결 성공..."))
  .catch((err) => console.log(err));

위 코드에 "복사한 url과 id/password"는 노출되어서는 안되는 데이터입니다.

서버 사이드에서 이러한 데이터는 보통 config 폴더를 생성해서 .gitignore에 추가해서 관리합니다.

이때 개발 환경을 두가지 경우로 생각해야합니다

  • Local : development 단계로 로컬에서 개발 진행 환경
  • Deploy : production 단계로 배포를 한 후의 환경

따라서 개발 환경과 배포 후의 환경에 따라 값을 가져오는 분기 처리를 해줘야합니다.

위 코드를 예시로 들자면

server/config/dev.js

module.exports = {
  mongoURI: "url + id/password"
}

dev는 development의 약자로 개발 환경을 의미합니다.

server/config/prod.js

module.exports = {
  mongoURI: process.env.MONGO_URI
}

prod는 production의 약자로 배포 후의 환경을 의미합니다.

server/config/key.js

process.env.NODE_ENV === 'production' 
  ? module.exports = require('./prod') 
  : module.exports = require('./dev');

process.env.NODE_ENV는 환경 변수로컬 환경에서는 development, 배포 후의 환경에서는 production이 나오기 때문에 위와 같이 분기 처리를 해줄 수 있습니다.

Index.js

const mongoose = require("mongoose");

const config = require("./config/key");

mongoose
  .connect(config.mongoURI)
  .then(() => console.log("MongoDB 연결 성공..."))
  .catch((err) => console.log(err));

.gitignore

dev.js 추가

이제 위 코드와 같이 로컬/배포 각 환경에서 환경 변수에 따라 분기 처리를 해줄 수 있고 .gitignore를 이용해서 민감한 데이터를 노출시키지 않을 수 있습니다.

깃헙에 push해서 레포에 dev.js가 올라가지 않느지 확인


데이터 암호화

위 그림에서 받은 데이터의 password는 암호화가 되어있지 않기 때문에 관리자등에게 비밀번호가 노출될 수 있어 password 암호화가 필요합니다.

bcrypt

데이터를 암호화 할 수 있도록 도와주는 모듈

Index.js - api/users/register

app.post("/api/users/register", (req, res) => {
  const user = new User(req.body);

  user.save( ... );
});

위 코드에서 확인해보면 req.body로 받은 데이터를 save()하고 있습니다.
따라서 데이터를 받기 전에 암호화를 한다면 암호화된 데이터를 넘겨 받을 수 있습니다.

이 기능은 mongoose의 기능bcrypt 모듈을 함께 사용하여 만들 수 있습니다.

User.js

const userSchema = mongoose.Schema({ ... });

userSchema.pre('save', function(next) { 
  // 비밀번호 암호화 과정
  ...something...

    next();
});

위 코드는 유저 모델에 데이터를 save() 전에 ...something... 과정을 거치고 next()를 통해 save()로 넘어간다는 의미입니다.

이제 위의 ...something... 과정안에 bcrypt 모듈을 사용해서 암호화를 진행합니다.

bcryptjs
npm i bcrypt

User.js

const bcrypt = require("bcrypt");

userSchema.pre("save", function (next) {
  var user = this;

  // 비밀번호 암호화 과정
  if (user.isModified("password")) {
    bcrypt.genSalt(10, function (err, salt) {
      if (err) return next(err);
      bcrypt.hash(user.password, salt, function (err, hash) {
        if (err) return next(err);
        user.password = hash;
        next();
      });
    });
  } else {
    next();
  }
});

코드 해석

if (user.isModified("password")) { ... } else { next() }
유저의 비밀번호가 변경되었을 때만 아래 과정을 수행하고 아닐 경우 next()로 빠져나가 일반적인 save()를 하게됩니다.

  • isModified() 함수는 mongoose 모듈에 포함되어있는 함수로 파라미터로 들어온 값이 DB에 기록된 값과 비교해서 변경된 경우는 true를, 그렇지 않은 경우는 false를 반환
  • user 생성 시에는 항상 true이며, user가 수정되는 경우에는 password가 변경되는 경우에만 true를 반환
bcrypt.genSalt(saltRounds, function (err, salt) {
      bcrypt.hash(myPlaintextPassword, salt, function (err, hash) {
         // Store hash in your password DB.
      });
    })
  • saltRounds : 10 ~ 20 사용 가능

문서에 나와있는 사용법으로 salt와 hash를 생성하고 myPlaintextPassword(암호화 전의 password 값)을 가져오기 위해 this를 사용하고 에러처리를 하고 성공한다면 생성된 암호화된 비밀번호(hash)를 유저의 비밀번호로 교체합니다.


Postman과 MongoDB에 암호화된 비밀번호가 저장되는지 확인

PostmanPostman
MongoDB
profile
둡둡

0개의 댓글