Bcrypt
처음부터 단방향 암호화를 위해 만들어진 해시 함수
- sha256 같은 해시 함수들은 원래 암호화를 위해 설계된 것이 아니라 짧은 시간에 데이터를 검색하기 위한 자료구조로 설계가 되었다. 이 때문에 빠른 속도가 장점이지만 취약점의 원인이기도 하다.
- rainbow table attack : 미리 해시 값들을 계산해 놓은 테이블을 가지고 해시 함수 반환 값을 역추적해 원래 정보를 찾아내는 해킹 방법이다.
- 이런 취약점을 보안한 것이 바로 bcrypt같은 해시 함수이다(salting과 키 스트레칭을 구현한 해시 함수 중 대표적인 함수)
salting과 키 스트레칭
- salting : 실제 정보 이외에 추가적으로 무작위 데이터를 더해서 해시 값을 계산하는 방법. salt로 인해 해시값이 달라지기 때문에, rainbow attack 같이 미리 해시 값을 계산해 하는 공격을 무효화 시킨다. salt 자체는 비밀이 아니고 해시 값을 바꾸는데 목적이 있다. salt는 비밀번호마다 모두 다르기 때문에 같은 비밀번호라도 해시값이 달라지게 된다. 이 때문에 공격자는 해시값 데이터베이스를 만들 수 없다.
- 키 스트레칭 : 기존 단방향 해시 알고리즘의 빠른 실행 속도가 취약점이 됐던것을 보완하기 위한 방법. 단방향 해시 값을 계산한 후 그 해시 값을 다시 해시하고, 이를 반복한다. 향후 컴퓨터 성능이 향상되어도 해시 반복 횟수를 추가하여 계속해서 보완할 수 있다.
bcrypt를 이용해 암호화,복호화
Installation
npm i bcryptjs
Source Code
const mongoose = require('mongoose');
const bcrypt = require('bcrypt'); //비밀번호 암호화
const saltRounds = 10; //암호화
const jwt = require('jsonwebtoken'); //토큰
const moment = require("moment");//토큰 만료기간
const userSchema = mongoose.Schema({
name: {
type:String,
maxlength:50
},
email: {
type:String,
trim:true,
unique: 1
},
password: {
type: String,
minglength: 5
},
lastname: {
type:String,
maxlength: 50
},
role : {
type:Number,
default: 0
},
cart :{ //장바구니
type:Array,
default:[]
},
history :{ //결제내역
type:Array,
default:[]
},
image: String,
token : {
type: String, //토큰
},
tokenExp :{
type: Number //토큰만료기간
}
})
//암호화 : bcrypt
userSchema.pre('save', function( next ) { //'save': db저장전에 작업실행
var user = this; //const user = new User(req.body); 이부분에서 this로 지정가능
if(user.isModified('password')){ //비밀번호 암호화는 비밀번호가 변경될때만 해야됨(무->유 마찬가지)
bcrypt.genSalt(saltRounds, function(err, salt){ //암호화
if(err) return next(err);
bcrypt.hash(user.password, salt, function(err, hash){ //암호화
if(err) return next(err);
user.password = hash
next()
})
})
} else {//다른것을 바꾼다면
next()
}
});
//비밀번호 검증 복호화: comparePassword: mongoose 문법 (작명부분)
userSchema.methods.comparePassword = function(plainPassword,cb){ //cb: callback function ,
bcrypt.compare(plainPassword, this.password, function(err, isMatch){ //plainPassword 암호화한뒤 this.password와 비교
if (err) return cb(err); //비밀번호 틀리면 callback error
cb(null, isMatch) //맞으면 no error , true 반환
})
}
//plainPassword: client에서 치는 비밀번호
//this.password: 암호화된 비밀번호
user database model
const express = require('express');
const router = express.Router();
const { User } = require("../models/User");
//회원가입
router.post("/register", (req, res) => {
const user = new User(req.body);
user.save((err, user) => { //db저장
if (err) return res.json({ success: false, err });
return res.status(200).json({
success: true
});
});
});
//로그인
router.post("/login", (req, res) => {
User.findOne({ email: req.body.email }, (err, user) => {
if (!user){ //user가 db에 없는경우(1차검증)
return res.json({
loginSuccess: false,
message: "Auth failed, email not found"
});
}
//2차검증
user.comparePassword(req.body.password, (err, isMatch) => { //비밀번호 검증 comparePassword: mongoose 문법(작문)
if (!isMatch) //비밀번호 틀리면
return res.json({ loginSuccess: false, message: "Wrong password" });
user.generateToken((err, user) => { //비밀번호 맞다면 토큰생성 generateToken: mongoose 문법(작문)
if (err) return res.status(400).json(err);
res.cookie("w_authExp", user.tokenExp); //토큰만료기간 저장(client cookie)
res.cookie("w_auth", user.token) //토큰 저장(client cookie)
.status(200) //성공했다면
.json({
loginSuccess: true, userId: user._id
});
});
});
});
});
user router
'Node.js' 카테고리의 다른 글
JSON Web Token(JWT) (0) | 2022.01.16 |
---|---|
Multer (0) | 2022.01.16 |
fluent-ffmpeg (0) | 2022.01.16 |