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');