보안
회원가입시에 pw를 받는데 이걸 DB에 그냥 저장해서는 안된다.
그래서 이를 해시하여 저장을 한다. 이러면 DB가 해킹을 당한다고 해도 원문 비밀번호를 모르므로 인증, 인가에 문제가 생길 확률이 낮아지게 된다.
하지만 해시 함수의 특성상 1이라는 값을 넣으면 항상 고정된 값이 나오므로 이걸 저장한 레인보우 테이블이라는게 존재한다.
예를들어 비밀번호가 'password'라면, 이걸 그냥 해시하게되면 항상 sdkflknut23h17이라는 값이나오게 된다.
항상 sdkflknut23h17이기 때문에 결국 sdkflknut23h17을 보면 password라고 유추해낼 수 있게 되는 것이다.
salt
이런 문제를 해결하기 위해 회원가입시에 랜덤한 문자열 값인 salt를 입력으로 들어온 pw에 덧붙여서 해시하고, 이를 DB에 저장한다.
이러면 비밀번호가 password로 같은 사람들이 있다고 하더라도 항상 다른 salt가 생성되므로 해시값이 다르게 저장되게 된다.
이렇게 생성된 salt를 DB에 저장해서 로그인시에 로그인 원문과 salt를 붙여서 해시한다음 저장된 pw와 비교하면 된다.
crypto
crypto라는 라이브러리를 통해 해시와 salt를 사용할 수 있다.
signup 구현
src/routes/signup.js
const express = require('express');
const signup = require('../SQL/signup');
const router = express.Router();
router.post('/', (req, res) => {
const { id, pw, name, age } = req.body;
try {
signup(id, pw, name, age);
res.status(200).json({
status: 200,
message: '회원가입 성공',
});
} catch (err) {
res.status(500).json({
status: 500,
message: '회원가입 실패',
});
}
});
module.exports = router;
src/SQL/signup.js
const connectMySQL = require('./connect');
const crypto = require('crypto');
const signup = async (id, pw, name, age) => {
const salt = crypto.randomBytes(128).toString('base64'); //salt 생성
const hasedPassword = crypto
.createHash('sha512') //sha512알고리즘
.update(pw + salt) //입력받은 pw에 salt를 합침
.digest('hex');
const connect = await connectMySQL();
const signupSQL = `INSERT INTO login_table(id,pw,name,age,salt) values('${id}', '${hasedPassword}', '${name}', '${age}','${salt}')`;
connect.execute(signupSQL);
};
module.exports = signup;
salt를 128바이트형태로 만들고 string으로 변환하여 pw에 더해주고 sha512알고리즘을 사용하여 해시한다.
login 구현
src/routes/index.js
const express = require('express');
const login = require('../SQL/login');
const crypto = require('crypto');
const router = express.Router();
router.post('/', async (req, res) => {
const { id: reqId, pw: reqPW } = req.body;
const result = await login();
let success = false;
result.forEach((info) => {
const { id, pw, salt } = info;
const compareHash = crypto
.createHash('sha512')
.update(reqPW + salt) //입력받은 pw와 db의 salt를 더하여 해시한다음
.digest('hex');
if (id === reqId && pw === compareHash) { //비교함
const response = {
status: 200,
message: '로그인 성공',
};
res.json(response);
success = true;
}
});
if (!success) {
res.status(401).send('로그인 실패');
}
});
module.exports = router;
src/SQL/login
const connectMySQL = require('./connect');
const login = async (id, pw) => {
const connect = await connectMySQL();
const SQL = `SELECT id,pw,salt from login_table`;
const [rows] = await connect.execute(SQL);
return rows;
};
module.exports = login;
사실 이렇게 하면 안된다.
SQL에서 전체 데이터를 가져와서 js에서 비교하고 있는데 이렇게 하기보다는 where문을 사용해서 구현하자.
나중에 개선할 생각이다.