crypto를 사용한 비밀번호 암호화

Creating the dots·2022년 2월 4일
0

Node js

목록 보기
3/3

Crypto

Crypto 모듈을 사용해 솔트를 생성하고, 해싱알고리즘을 사용해 암호화한 후 인코딩한 결과를 데이터베이스에 저장할 수 있다.

randomBytes (ASYNC)

randomBytes(bytes, callback)
randomBytes라는 메소드는 콜백함수를 인자로 받을 수 있다. 콜백함수가 주어질 경우, 비동기적으로 동작시킬 수 있다. 아래의 코드를 보면, randomBytes내의 콜백함수 실행결과가 마지막에 찍힌다.

console.log("start");
crypto.randomBytes(128, (err, buf) => {
  if(err) {
    console.log(err); 
    return;
  }
  console.log("The random data is: "+buf.toString("base64"));
});
console.log("end");
//start
//end
//The random data is ...

randomBytes (SYNC)

randomBytes(bytes)
randomBytes에 콜백함수를 전달하지 않으면, 동기적으로 실행된다. 따라서 randomBytes가 생성될때까지 다음 코드를 실행하지 못한다. 따라서, 아래의 코드를 실행해보면 순차적으로 콘솔이 찍히는 것을 확인할 수 있다.

console.log("start");
const buf = crypto.randomBytes(128);
console.log(buf);
console.log("The random data is: "+buf.toString("base64"));
console.log("end");
//start
//<Buffer d3 .......>
//The random data is ...
//end

createHash, update, digest

동기적으로 salt를 만든 후 비밀번호+salt를 sha512 해시 알고리즘으로 해싱한 후 base64로 인코딩해 데이터베이스에 저장한다.

//controllers/signup.js
const crypto = require("crypto");
const { User } = require("../../models"); //seqeulize 사용함

module.exports = async (req, res) => {
  try {
    const { nickname, email, password } = req.body;
    const salt = crypto.randomBytes(128).toString("base64"); //동기적으로 실행됨
    const hashPassword = crypto
      .createHash("sha512") //해시 알고리즘
      .update(password + salt) //변환할 문자열
      .digest("base64"); //인코딩 알고리즘

    const data = await User.create({
      nickname,
      email,
      salt,
      password: hashPassword,
    });
    return res.status(201).end();
  } catch (err) {
    throw err;
  }
};

pbkdf2 (ASYNC)

pbkdf2Sync(password, salt, iterations, keylen, digest, callback)

const db = require("../../db/db");
const crypto = require("crypto");
module.exports = (req, res) => {
  const { email, password } = req.body;
  console.log(password);
  crypto.randomBytes(128, (err, buf) => {
    if (err) {
      console.log(err);
      return;
    }
    const salt = buf.toString("base64");
    crypto.pbkdf2(
      password,
      salt,
      100000,
      64,
      "sha512",
      async (err, derivedKey) => {
        if (err) throw err;
        const hashedPassword = derivedKey.toString("base64");
        console.log(hashedPassword);
        const sql = "INSERT INTO user (email, salt, password) VALUES (?,?,?)";
        const params = [email, salt, hashedPassword];
        await db.query(sql, params);
        return res.status(201).end();
      }
    );
  });
};

pbkdf2 (SYNC, Async, await)

node의 내장 모듈인 util을 사용하여 함수의 리턴값을 프로미스의 형태로 변환해 .then 또는 async, await를 사용할 수 있다. 위의 코드에서 콜백함수 안에 콜백함수를 호출하는 것을 가독성있게 다음과 같이 변경할 수 있다.
(아래의 모듈은 회원가입하거나 비밀번호를 바꿀때 공통적으로 사용되는 내용을 createHash로 저장한 것이다.)

//funcs/createHash.js
const crypto = require("crypto"); //node 내장모듈
const util = require("util"); //node 내장모듈
module.exports = async (password) => {
  try {
    const randomBytesPromise = util.promisify(crypto.randomBytes);
    const pbkdf2Promise = util.promisify(crypto.pbkdf2);

    const buf = await randomBytesPromise(64);
    const salt = buf.toString("base64");
    const hashedPassword = await pbkdf2Promise(
      password,
      salt,
      100000,
      64,
      "sha512"
    );
    return [salt, hashedPassword.toString("base64")];
  } catch (err) {
    throw err;
  }
};

pbkdf2Sync (SYNC)

pbkdf2Sync(password, salt, iterations, keylen, digest)
crypto에서 제공하는 pbkdf2메소드에서는 콜백함수를 사용해 비동기적으로 실행했다면, pbkdf2Sync 메소드를 사용해 동기적으로 해싱할 수 있다. 바로 위에서 .then 또는 await를 사용하기 위해 util에서 promisify 메소드를 사용했던 과정을 생략할 수 있다.

const salt = crypto.randomBytes(128).toString('base64');
const hashPassword = crypto
.pbkdf2Sync(password, salt, 100000, 64, 'sha512')
.toString('hex');
profile
어제보다 나은 오늘을 만드는 중

0개의 댓글