[Mongoose Encryption] DB를 활용한 유저인증 방식

2020. 5. 16. 22:38컴퓨터언어/Database

728x90
반응형

https://stackoverflow.com/questions/454048/what-is-the-difference-between-encrypting-and-signing-in-asymmetric-encryption

 

What is the difference between encrypting and signing in asymmetric encryption?

What is the difference between encrypting some data vs signing some data (using RSA)? Does it simply reverse the role of the public-private keys? For example, I want to use my private key to

stackoverflow.com

회원 정보가 Database에 저장되어 있다고 가정할 때, 로그인 인증을 하는 6가지 방법을 알아보자.


1단계 : body-parser로 route의 request 인자에 접근하는 방법.

const express = require("express");
const bodyParser = require("body-parser");
const ejs = require("ejs");
const mongoose = require("mongoose");
const app = express();

app.use(bodyParser.urlencoded({ extended: true }));
app.set("view engine", "ejs");

const userSchema = new mongoose.Schema({
  email: String,
  password: String
});

const User = new mongoose.model("User", userSchema);

app.route("/login")
  .get(function (req, res) {
  	res.render("login")
  })

  .post(function (req, res) {
  	const username = req.body.username;
    const password = req.body.password;
    
    User.findOne({ email: username }, function (err, result) {
    	
        if (err) {
        	console.log(err)
        } else {
        	if (result.password === password) {
            	res.render("success")
            }
        }
    }
  });
  
  app.listen(3000, () => console.log("Server is connected on port 3000."));

<한계>

비밀번호 같은 데이터도 그대로 보이므로 보안이라는 개념이 없는 위험한 방식.


2단계 : mongoose-encryption 모듈을 이용한 데이터 암호화.

https://www.npmjs.com/package/mongoose-encryption

 

mongoose-encryption

Simple encryption and authentication plugin for Mongoose

www.npmjs.com

<Keypoint>

암호화 단계는 스키마 설정과 모델 설계 사이에 넣어야 한다.

왜냐하면 모델을 정의할 때는 .model() 메서드에 스키마를 인자로 사용하는데, 암호화는 바로 이 스키마를 가지고 하는 것이기 때문이다.

mongoose.connect("mongodb://localhost:27017/userDB", {useNewUrlParser: true, useUnifiedTopology: true});

const userSchema = new mongoose.Schema({
  email: String,
  password: String
});

const secret = "lalala~something"

userSchema.plugin(encrypt, {secret: secret, encryptedFields: ["password"]})

스키마에 암호화를 설정하는 코드는 다음과 같다. (mongoose-encryption npm 링크 참조)

save()를 하면 암호화를, find()를 하면 복호화를 스스로 진행한다.

app.route("/login")
  .get(function (req, res) {
    res.render("login")
  })

  .post(function (req, res) {
    const userName = req.body.username;
    const password = req.body.password;
    User.findOne({email: userName}, function (err, result) {
      if (err) {
        console.log(err);
      } else {
        if (result) {
          if (result.password === password) {
            res.send("good")
          }
        }
      }
    })
  })

<한계>

암호화&사이닝 키를 한번에 퉁치는 secret값을 파일 내부에 가지고 있기 때문에, 해당 js 파일이 해킹되면 끝장난다.


3단계 : dotenv(환경변수 모듈)을 활용한 민감한 정보 따로 관리하기

https://github.com/motdotla/dotenv

 

motdotla/dotenv

Loads environment variables from .env for nodejs projects. - motdotla/dotenv

github.com

API Key나 Encrypt Key는 파일 구동 시 꼭 필요한 코드이지만, 해당 js 파일의 보안성이 100% 보장될 수 없기에 우리는 새로운 안정된 방법이 필요하다.

이럴 때는 npm 모듈패키지 중 dotenv를 사용하면 된다.

이 패키지는 함부로 공개되어서는 안되는 데이터들을 .env 파일에 따로 관리할 수 있게 하기 때문에 굉장히 유용하다.

해당 코드에 접근하는 방법은 아래와 같다.

process.env.SECRET

물론 .gitignore에 .env 파일을 포함하는 것은 필수다.

// .env
SECRET=thisismycustomencryptkeylol
// app.js
// 파일 최상단에서 dotenv 모듈을 활성화해야한다.
require("dotenv").config()

const userSchema = new mongoose.Schema({
    email: String,
    password: String
});

userSchema.plugin(encrypt, { secret: process.env.SECRET, encryptFields: ["password"] });

<한계>

어쨌든 암호화를 위한 Key값을 한 파일 내부에 날것으로 보관하고 있다는 것은 해킹의 위험성이 있기 때문에 완벽하지 않다.

 


4단계 : 해시함수 이용.

여러 수를 곱하여 결과를 도출하는 것은 상당히 빠르며 쉽고, 컴퓨터가 가장 자신있는 연산이다.

하지만 반대로 하나의 숫자가 어떤 수들의 곱으로 이루어지는지를 역산하는 것은 경우의 수가 무수하고 알아내기 어렵다.

 

이를 암호화에 이용하면, 암호화에는 상당히 빠르며 쉬우면서도, 실제 정보를 다시 알아내기에는 거의 불가능한 좋은 방법이 된다.

여기서 거의 불가능하다는 것은 현대 컴퓨팅 자원으로 천문학적인 시간이 소요됨을 의미한다.

 

예를 들어, 회원가입 시 해시함수는 비밀번호같이 노출되어서는 안되는 정보들을 각각의 특정 해시로 변환한 후 Database에 저장하게 되고, 이후 로그인을 할 때도 로그인을 요청할 때마다 입력한 비밀번호를, 동일한 해시함수로 변환하여 도출한 해시값과 회원가입 시 저장된 해시값을 비교하여 처리하게 된다.

 

그런데 해시는 단지 암호화를 위한 것만은 아니다. 왜냐하면 문자열 등의 실제 저장정보를 SHA1 등 특정 함수에 넣는 것이기 때문에, 같은 원본 데이터는 언제나 동일한 해시를 낳기 때문이다.

따라서 해시를 이용하면 원본 데이터가 변경 또는 손상되었는지의 여부도 따질 수 있다.

https://www.npmjs.com/package/md5

 

md5

js function for hashing messages with MD5

www.npmjs.com

md5 모듈은 MD5 알고리즘을 이용한 패키지로, DB에 새로 저장되는 값의 해시와 클라이언트가 입력하는 값의 해시를 비교하게 된다.

const md5 = require("md5");

// 회원가입(패스워드 새로 등록)
app.route("/register")
  .get(function (req, res) {
    res.render("register")
  })

  .post(function (req, res) {
    const newUser = new User({
      email: req.body.username,
      password: req.body.password
    });
    
    newUser.save(function (err) {
      if (err) {
        console.log(err);
      } else {
        res.render("secrets");
      }
    });
  });

// 로그인(패스워드 일치 검사)
app.route("/login")
  .get(function (req, res) {
    res.render("login")
  })

  .post(function (req, res) {
    const userName = req.body.username;
    const password = req.body.password;
    User.findOne({email: userName}, function (err, result) {
      if (err) {
        console.log(err);
      } else {
        if (result) {
          if (result.password === password) {
            res.send("good")
          }
        }
      }
    })
  })

비밀번호를 123으로 설정했을 때의 해시값. 123은 항상 저 값으로 바뀐다.

728x90
반응형