2023-08-29 20:55:55

bcrypt

bcrypt란 패스워드를 해싱하기 위한 암호화 라이브러리를 말하며 원래 데이터를 고유한 문자열로 생성하는 프로세싱을 거쳐 패스워드를 안전하게 저장하기 위해 사용됩니다.

bcrypt는 많은 개발자들이 사용하고 있어 이 알고리즘의 안전성과 효과성을 많이 알려져 있다고 많이 사용하는 이유는 다음과 같다

 

  • 평문 저장 방지 : 사용자의 패스워드를 데이터베이스에 평문으로 직접 저장하는 것은 큰 보안의 위험을 초래하기 때문에 해싱을 통해 패스워드의 노출을 방지 할 수 있습니다.
  • 솔트(salt) : bcrypt는 자동으로 솔트를 생성하고 이를 해싱 프로세스에 적용합니다. 솔트는 사용자의 패스워드에 랜덤한 문자열을 추가하여 동일한 패스워드라도 다른 해시 값을 생성하게되어 무차별 대입공격을 방지 할 수 있습니다.
  • 적응성 : bcrypt는 해싱의 복잡성을 조정할 수 있는 cost factor를 가지고 있는데 이 요소는 해싱을 수행하는데 필요한 반복 횟수를 결정합니다. costfactor가 높을 수록 해싱에 소요되는 시간이 길어지며 결과적으로 공격자가 비밀번호를 추측하려면 더 많은 시간이 필요하게됩니다.

 

bcrypt의 장점

  • 보안성 : 솔트 사용과 적응성 덕분에 다른 해싱 알고리즘에 비해 높은 보안성을 제공합니다.
  • 간단한 사용법 : bcrypt 라이브러리는 패스워 해싱 및 검증과 관련된 API를 간단하게 제공하므로 복잡한 암호화나 해싱 로직을 직접 구현할 필요가 없습니다.

 

실습

  • bcrypt을 사용하여 간단한 회원가입 및 로그인 기능을 수행
  • 실습 순서는 회원가입을 한 뒤 데이터베이스에 있는 아이디와 패스워드가 맞는지 확인하고 맞으면 로그인 성공했다는 로그를 띄워줌

 

프로젝트 구조

 

/app.js

const express = require("express");
const path = require("path");

const mainRouter = require("./routers/mainRouter");
const signinRouter = require("./routers/signinRouter");
const signupRouter = require("./routers/signupRouter");

const app = express();

app.set("views", path.join(__dirname, "pages"));
app.set("view engine", "ejs");

app.use(express.urlencoded({ extended: false }));

app.use(mainRouter);
app.use("/signin", signinRouter);
app.use("/signup", signupRouter);

app.listen(8080, () => {
  console.log("Server Start");
});

 

/controllors/userContollers.js

const { signup, signin } = require("../models/userModel");

exports.signUp = async (req, res) => {
  try {
    const { user_id, user_pw } = req.body;
    await signup(user_id, user_pw);
  } catch (error) {
    console.error(error);
  }
};

exports.signIn = async (req, res) => {
  try {
    const { user_id, user_pw } = req.body;
    await signin(user_id, user_pw);
  } catch (error) {
    console.error(error);
  }
};

 

/models/config.js

const mysql2 = require("mysql2/promise");

const mysql = mysql2.createPool({
  host: "127.0.0.1",
  user: "root",
  password: "root",
  database: "blog",
  multipleStatements: true,
});

module.exports = mysql;

 

/models/userModel.js

const mysql = require("./config");
const bcrypt = require("bcrypt");

// 회원가입
// 입력 받은 패스워드를 bcrypt를 활용하여 해싱
exports.signup = async (user_id, user_pw) => {
  try {
    // 가입된 아이디가 있는지 확인
    const [user] = await mysql.query("select * from users where user_id = ?", [
      user_id,
    ]);
    // user의 길이가 0이 아니면 무언가 가져왔다는 뜻
    if (user.length != 0) {
      return;
    } else {
      // 첫번째 매개변수 : 비밀번호
      // 두번째 매개변수 : 솔트 횟수
      const hash = bcrypt.hashSync(user_pw, 10);
      await mysql.query("insert into users (user_id, user_pw) values (?,?);", [
        user_id,
        hash,
      ]);
    }
  } catch (error) {
    console.error(error);
  }
};

// 로그인
// 입력받은 비밀번호가 해싱된 암호와 맞는지 확인
exports.signin = async (user_id, user_pw) => {
  try {
    const [user] = await mysql.query("select * from users where user_id = ?", [
      user_id,
    ]);

    if (user[0]?.user_id) {
      // 입력받은 비밀번호가 해싱된 비밀번호와 맞는지 boolean 값으로 반환
      const campare = bcrypt.compareSync(user_pw, user[0].user_pw);
      if (campare) {
        console.log("로그인 성공");
      } else {
        return "비밀번호가 틀렸습니다.";
      }
    } else {
      return "없는 아이디입니다.";
    }
  } catch (error) {
    console.error(error);
  }
};

/pages/main.ejs

더보기
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>main</title>
</head>
<body>
<div>
<h1>메인페이지</h1>
<h3>안녕하세요^-^</h3>
<br />
<a href="/signup">signup</a>
<a href="/signin">signin</a>
</div>
</body>
<script></script>
</html>

/pages/signin.ejs

더보기
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>login</title>
</head>
<body>
<h1>로그인</h1>
<form action="/signin/signin" method="post">
<label for="">ID</label>
<input type="text" name="user_id" />
<label for="">PW</label>
<input type="text" name="user_pw" />
<button>로그인하기</button>
</form>
</body>
</html>

/pages/signup.ejs

더보기
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>singup</title>
</head>
<body>
<h1>회원가입</h1>
<form action="/signup/signup" method="post">
<label for="">ID</label>
<input type="text" name="user_id" />
<label for="">PW</label>
<input type="text" name="user_pw" />
<button>가입하기</button>
</form>
</body>
<script></script>
</html>

/routers/mainRouter.js

const router = require("express").Router();

router.get("/", (req, res) => {
  res.render("main");
});

module.exports = router;

/routers/signinRouter.js

const router = require("express").Router();
const { signIn } = require("../controllers/userController");

router.get("/", (req, res) => {
  res.render("signin");
});

router.post("/signin", signIn);

module.exports = router;

/routers/signupRouter.js

const router = require("express").Router();
const { signUp } = require("../controllers/userController");

router.get("/", (req, res) => {
  res.render("signup");
});

router.post("/signup", signUp);

module.exports = router;

 

실습 화면

메인페이지에서 회원가입 페이지로 이동
ID : test, PW : test로 회원가입 시도
해시화된 암호로 데이터베이스에 저장
가입한 test 계정으로 로그인 시도
로그인 성공 로그 출력 확인

728x90