원래 회사에서 사용자 비밀번호 암호화 방식으로 bcryp를 사용하고 있었는데, 고객사에서 sha256방식으로 변경을 요청했다. sha256을 사용하는데 여러가지 방식이 있겠지만 내가 현재 진행하는 솔루션은 사용자 쪽을 많이 쓰지않아서 간단하게 crypto-js를 이용해서 테스트 코드를 만들었다.
Front-End는 Vue.js, Back-End는 node.js를 사용해서 모두 javascript로 구현했다.
먼저 Front-End와 Back-End쪽에 crypto-js를 설치해준다
npm i crypto-js
https://www.npmjs.com/package/crypto-js
// 암호화 관련 기능을 제공하는 자바스크립트 라이브러리
import CryptoJS from "crypto-js";
let user = {
username: '',
userid: '',
password: '',
password2: ''
}
signUp(){
// 사용자가 입력한 username, userid, password, password(비밀번호 확인) 값을 체크
if(this.user.username === '' || this.user.userid === '' || this.user.password === '' || this.user.password2=== ''){
this.changeDialogText("회원가입을 하려면 모든 값을 입력해주세요")
return;
}
if(this.user.password !== this.user.password2){
this.changeDialogText("비밀번호가 일치하지 않습니다")
return;
} else {
// CryptoJS 모듈의 SHA256 함수를 이용하여 사용자가 입력한 비밀번호를 해시값으로 변환
const hashPw = CryptoJS.SHA256(this.user.password).toString();
// axios 모듈을 이용하여 서버로 회원가입 요청
this.$axios.post(`/api/user/signUp`, {
user: {
userName: this.user.username,
userID: this.user.userid,
password: hashPw,
userAuth: 100,
}
})
// 서버로부터 응답이 오면 처리
.then((res) => {
if(res.data.success){
this.init()
alert("회원가입이 정상적으로 완료되었습니다.")
}else{
alert("회원가입이 실패하였습니다.")
}
})
.catch(function (error) {
console.log('error')
})
}
},
const CryptoJS = require('crypto-js');
// HTTP POST 메소드를 사용하여 '/signUp' 경로로 요청이 들어오면 처리하는 함수
router.post('/signUp', function (req, res) {
let body = req.body
let encryptedPassword = ''
// 요청에서 받아온 비밀번호를 SHA256 해시 함수를 사용하여 암호화
encryptedPassword = CryptoJS.SHA256(body.user.password).toString();
// 'Users' 테이블에 새로운 회원 정보를 INSERT
let query = `INSERT INTO Users (UserID,UserName,Password,UserAuth) VALUES (?,?,?,?)
ON DUPLICATE KEY UPDATE UserName=?, UserAuth=?`
let param = [body.user.userID, body.user.userName, encryptedPassword, body.user.userAuth, body.user.userName, body.user.userAuth]
// mySQL DB에 쿼리를 날리는 함수
// 쿼리문과 매개변수를 이용하여 데이터베이스에 쿼리를 전송하고, 전송한 결과를 콜백함수에서 처리
pool.executeQuery(query, param, function (err, row) {
if (err) {
res.json({
success: false,
message: 'Sign Up Failed Please use another ID'
})
}
else {
res.json({
success: true,
message: 'Sing Up Success!',
})
}
});
});
DB에 아래처럼 회원가입이된다.
Front-end에서 비밀번호를 해시화하는 이유는 클라이언트에서 입력한 원본 비밀번호를 서버로 전송할 때, 원본 비밀번호를 보이지 않게 하기위해서이다. 그러나 이것만으론 보안이 보장되지 않는게 해커가 해시화된 비밀번호를 가로채서 그 값을 그대로 서버에 보낼 수도 있기때문이다. 그러면 해커는 가로챈 비밀번호로 로그인 성공...😭
그래서 서버에서도 비밀번호를 해시화해서 해시화된 비밀번호를 비교해서 로그인 가능여부를 확인하는 게 좋다.
그리고 추가로 Front-end에서 해시화하는 것하고 Back-end에서 해시화하는게 알고리즘? salt? 등의 설정이 좀 다른거 같다. (salt넣고 해보는데 값이 이상하게 나옴😧) 해시화된 결과가 일치하지 않는 문제가 있어서 Front-end와 Back-end는 왠만하면 동일한 설정으로 해시화를 수행하는 것이 좋을 것같다.
import CryptoJS from "crypto-js";
login () {
if(this.user.userid === ``) return alert("아이디를 입력해 주세요.")
if(this.user.password === ``) return alert("암호를 입력해 주세요.")
// 입력된 비밀번호를 SHA256 해시 함수를 사용하여 암호화
const hashedPassword = CryptoJS.SHA256(this.user.password).toString();
// HTTP POST 요청을 전송
this.$axios.post(`/api/user/login`, {
user: {
userid: this.user.userid,
password: hashedPassword
}
})
.then((res) => {
if (res.data.success == true) {
this.$router.push('/')
}
if (res.data.success == false) {
alert(res.data.message);
}
})
.catch(function (error) {
console.log('error')
})
}
import CryptoJS from "crypto-js";
router.post('/login', function (req, res) {
const user = {
'userid': req.body.user.userid,
'password': req.body.user.password
};
let description = ''
let message = ''
let success = false
// CryptoJS 라이브러리를 사용하여 비밀번호를 해시화 한 후, MySQL 데이터베이스에서 해당 사용자가 등록되어 있는지를 확인
const hashedPassword = CryptoJS.SHA256(user.password).toString();
pool.executeQuery('SELECT UserID, Password, UserAuth FROM Users WHERE UserID = ?', [user.userid], function (err, row) {
if (err) {
// 쿼리 수행중 에러가 난 경우
description = 'Login ERROR'
message = 'Login ERROR!'
success = false
}
// 쿼리가 정상적으로 수행되었다면, 결과 값을 분석
// row[0] = {UserID: 'test', Password: '173ag6~~~', UserName: 'test', UserAuth: 100}
if (row[0] !== undefined && row[0].UserID === user.userid) {
if (hashedPassword === row[0].Password) {
// 로그인 성공
req.session.user = {
userid: user.userid,
userAuth: row[0].UserAuth
}
description = 'Login success'
message = 'Login successful!'
success = true
}
else {
// 매칭되는 아이디는 있으나, 비밀번호가 틀린 경우
description = 'Login fail'
message = 'Login failed please check your id or password!'
success = false
}
} else {
// 매칭되는 아이디 없을 경우
description = 'Login try or Check disable ID'
message = 'Login failed please check your id or password!'
success = false
}
pool.executeQuery('INSERT INTO LoginLog (UserID, RegisteredDate, Description) VALUES (?,now(),?)', [user.userid, `${user.userid} : ${description}`], function () {
})
res.json({
success: success,
message: message
})
})
});