사고쳤어요
[도서 쇼핑몰] Node.js - crypto로 비밀번호 암호화하기(회원가입, 로그인, 비밀번호 초기화) 본문
https://makeaccident.tistory.com/157
[도서 쇼핑몰] Node.js - 회원가입, 로그인, 비밀번호 초기화 API
회원가입const join = (req, res) => { const { email, password } = req.body; const sql = `INSERT INTO users (email, password) VALUES (?, ?);`; const values = [email, password]; conn.query(sql, values, (err, results) => { if (err) { return res.status(Stat
makeaccident.tistory.com
지난 번에 구현한 회원가입과 로그인 과정에서는 비밀번호를 있는 그대로 사용하기 때문에 노출될 수 있다는 문제가 있다.
Node.js에서 제공하는 crypto를 사용하여 비밀번호를 안전하게 암호화해보자.
const crypto = require("crypto");
crypto는 별도의 모듈 설치 없이 내장되어있어 사용할 수 있다.
const salt = crypto.randomBytes(64).toString("base64");
const hashPassword = crypto.pbkdf2Sync(password, salt, 10000, 64, "sha512").toString("base64");
비밀번호를 사용하는 예제는 다음과 같다.
salt라는 변수에 최대 64 길이의 랜덤 바이트를 문자열로 저장하고,
pbkdf2Sync() 함수에 값들을 넣어 암호화를 한다.
pbkdf2Sync() 함수에 대한 설명은 다음과 같다.
위 함수가 처리되는 과정을 이해하기 위해서 먼저 salt 개념에 대해 알아보자.
salt 값은 비밀번호를 해시 처리하는 단방향 함수의 추가 입력으로 사용되는 랜덤 데이터이다.
위에서는 이 값을 64바이트의 랜덤한 문자열로 생성해주었다.
실제로 salt를 출력해보면 매번 다른 문자열이 생성되고, 이 값들은 password에 붙어 함께 해시 함수를 거친다.
이제 다시 pbkdf2Sync()함수를 확인해보자.
파라미터로 들어가는 값은 비밀번호, salt, iterations, keylen, digest이다.
그리고 이는 비밀번호에 salt를 추가하여 iterations만큼 digest 해시 알고리즘을 적용하여 keylen 길이의 문자열을 리턴한다는 뜻이다.
예를 들어 pbkdf2Sync("1234", "abc", 10000, 64, "sha512")는 "1234"에 "abc"를 추가한 "1234abc"를 10000번 sha512를 돌려 64바이트의 문자열을 리턴하는 것이다.
그런데 해시 함수의 특징은 복호화가 안된다는 것이다.
그렇다면 비밀번호를 이렇게 암호화하였을 때 비밀번호가 맞는 지 아닌 지 어떻게 알 수 있을까?
비결은 회원가입을 할 때 랜덤으로 생성한 salt 값을 데이터베이스에 같이 저장해주는 것이다.
위에서 예를 들었던 "abc"를 salt로 사용할 경우 이 값과 암호화된 패스워드를 db에 함께 넣는다.
후에 사용자가 로그인을 하려고 아이디와 비밀번호를 입력하면, 이 아이디에 해당하는 salt값을 불러와 암호화를 한 뒤
데이터베이스에 저장해둔 암호화된 패스워드와 비교하면 되는 것이다!
기존 회원가입 코드
const join = (req, res) => {
const { email, password } = req.body;
const sql = `INSERT INTO users (email, password) VALUES (?, ?);`;
const values = [email, password];
conn.query(sql, values, (err, results) => {
if (err) {
return res.status(StatusCodes.BAD_REQUEST).json({
msg: `Error: ${err.code}`,
});
}
return res.status(StatusCodes.CREATED).json(results);
});
};
개선된 회원가입 코드
const join = (req, res) => {
const { email, password } = req.body;
// 비밀번호 암호화
const salt = crypto.randomBytes(10).toString("base64");
const hashPassword = crypto
.pbkdf2Sync(password, salt, 10000, 10, "sha512")
.toString("base64");
const sql = `INSERT INTO users (email, password, salt) VALUES (?, ?, ?);`;
const values = [email, hashPassword, salt];
conn.query(sql, values, (err, results) => {
if (err) {
return res.status(StatusCodes.BAD_REQUEST).json({
msg: `Error: ${err.code}`,
});
}
return res.status(StatusCodes.CREATED).json(results);
});
};
개선된 로그인 코드
const login = (req, res) => {
const { email, password } = req.body;
const sql = `SELECT * FROM users WHERE email = ?;`;
const values = email;
conn.query(sql, values, (err, results) => {
if (err) {
return res.status(StatusCodes.BAD_REQUEST).json({
msg: `Error: ${err.code}`,
});
}
const loginUser = results[0];
const salt = loginUser.salt;
const hashPassword = crypto
.pbkdf2Sync(password, salt, 10000, 10, "sha512")
.toString("base64");
if (loginUser && hashPassword == password) {
const token = jwt.sign(
{
email: loginUser.email,
},
process.env.PRIVATE_KEY,
{
expiresIn: "5m",
issuer: "minje",
}
);
res.cookie("token", {
httpOnly: true,
});
return res.status(StatusCodes.OK).json(results);
} else {
return res.status(StatusCodes.UNAUTHORIZED).end();
}
});
};
비밀번호 초기화
const passwordReset = (req, res) => {
const { email, password } = req.body;
const salt = crypto.randomBytes(10).toString("base64");
const hashPassword = crypto
.pbkdf2Sync(password, salt, 10000, 10, "sha512")
.toString("base64");
const sql = `UPDATE users SET password = ?, salt = ? WHERE email = ?;`;
const values = [hashPassword, salt, email];
conn.query(sql, values, (err, results) => {
if (err) {
return res.status(StatusCodes.BAD_REQUEST).json({
msg: `Error: ${err.code}`,
});
}
if (results.affectedRows == 0) {
return res.status(StatusCodes.BAD_REQUEST).end();
} else {
return res.status(StatusCodes.OK).json(results);
}
});
};
'웹 풀스택' 카테고리의 다른 글
[도서 쇼핑몰] Node.js - 도서 조회 페이지네이션 구현 (0) | 2025.03.13 |
---|---|
[도서 쇼핑몰] Node.js - 도서 조회 관련 API 구현하기 (0) | 2025.03.13 |
[도서 쇼핑몰] Node.js - 회원가입, 로그인, 비밀번호 초기화 API (0) | 2025.03.12 |
[Node.js] http-status-codes 사용해보기 (1) | 2025.03.12 |
[도서 쇼핑몰] dbdiagram으로 Mysql Workbench 세팅하기 (0) | 2025.03.12 |