DB에 사용자가 입력한 정보를 아래와 같이 POST했었다.
{
"name": "email1234",
"email": "email1234@naver.com",
"password": "1234567"
}
근데 저장된 DB를 보면 아래처럼 저장되어 있다.
누가봐도 보안에 매우 취약해보인다.
특히 password는 관리자조차도 알 수 없게 해야하는데 사용자가 입력한 그대로 입력되어있다.
그래서 password를 관리자도 password가 무엇인지 읽을 수 없게 변환해야한다.
이때 유용한 것이 'Bcrypt'이다.
우선 아래 명령어를 실행하여 'Bcrypt'를 설치한다.
npm i bcrypt --save
MongoDB의 Schema를 설정한 User.js로 가서 bcrypt를 require
한다.
const bcrypt = require('bcrypt');
우선 npm 홈페이지의 Bcrypt 사용법은 아래처럼 작성되어 있다.
나는 salt와 hash를 자동으로 생성하지 않기 때문에 salt를 우선 생성하고 생성된 salt로 password를 암호화 하는 Technique 1로 진행했다.
bcrypt.genSalt(saltRounds, function(err, salt) {
bcrypt.hash(myPlaintextPassword, salt, function(err, hash){
// Store hash in your password DB.
});
})
여기서 genSalt()
는 bcrypt가 password
를 암호화하기 위해 필요한 'salt'라는 것을 생성할 때 사용하는 메서드이다.
인자로는 saltRound
와 err
의 정보와 salt
를 담은 콜백함수이다.
genSalt()
의 첫번째 인자인saltRounds
는 기존에 salting된 password
를 몇 번 더 salting을 해서 해시를 도출할 것인가를 결정하는 인자라고 생각하면 된다.
여기서 사용되는 salt
란 명칭은 음식에 소금을 치면 사방으로 흩어지듯이 설정한 password
에 난수를 무작위로 추가하는 역할을 한다.
이 salt
라는 것이 설정한 password를 복잡하게 변경시키는 것이다.
그래서 saltRound
를 설정하고 다음으로 넘어간다.
// saltRounds를 10번 실행한다.
const saltRounds = 10;
그리고 salt를 생성하기 전에 작성했던 register 라우터를 보면 순서를 잘 생각해야한다.
app.post("/register", (req, res) => {
const user = new User(req.body);
// 암호화 실행 후...
// DB에 데이터 저장
user.save((err, userInfo) => {
if (err) return res.json({ success: false, err });
return res.status(200).json({
success: true,
});
});
});
여기서 user.save()
는 사용자가 입력한 데이터가 DB에 저장되는 것을 말하는데 이 메서드가 실행되기 전에 암호화가 진행되어야하는게 순서상 가장 맞다.
그러므로 salt를 생성하는 genSalt 부분을 pre()
메서드를 사용해서 감싸서 진행한다.
pre()는 mongoose에서 온 메서드이다.
userSchema.pre("save", function (next) {
const user = this;
bcrypt.genSalt(saltRounds, function(err, salt) {
bcrypt.hash(myPlaintextPassword, salt, function(err, hash){
// Store hash in your password DB.
});
})
next();
});
pre()
메서드를 사용해서 User모델(schema)에 사용자 정보를 save
하기 전에 두번째 인자인 콜백함수를 실행한다.
콜백함수의 파라미터인 next
는 pre()
함수가 끝이나면 실행하는 함수로 index.js
의 user.save
로 결과를 전송한다.
이제 pre()
메서드로 감싼 후 내부 함수를 작성한다.
bcrypt.genSalt(saltRounds, function(err, salt) {
// salt 생성 시 에러가 발생할 경우 user.save에 err 정보를 전송하고 아니면 다음 단계로 넘어간다.
if(err) return next(err);
//
bcrypt.hash(user.password, salt, function(err, hash){
if (err) return next(err);
user.password = hash;
next();
});
})
hash()
메서드는 입력받은 password
를 salting하여 암호화된 hash
로 생성한다.
이 hash
를 생성하는 도중 에러가 발생하면 에러를 user.save
으로 전송하고 아니라면 현재 model에 저장된 password
에 hash를 오버라이드해주고 다음 단계인 user.save
부분으로 DB에 저장한다.
그럼 아래의 데이터를 입력했을 때 데이터의 결과는 어떤지 보자.
{
"name": "test1",
"email": "test1@naver.com",
"password": "1234567"
}
"1234567"으로 설정했던 password
가 암호화되어 복잡한 문자열로 변한 것을 볼 수 있다.
요즘 공부를 하면서 가장 느끼는 점이 '공식 문서'의 중요성이다. 강의를 그냥 들으면서 공부하거나 혼자 공부하면서 모르는 부분을 공식 문서를 이해하지 못하더라도 보기라도 하면 에러가 발생했을 때 어느 정도의 예측이 가능한 것 같은 느낌이 든다.
앞으로 공식문서를 참고하면서 공부를 해나가야겠다.