React

[React] crypto 를 이용하여 암호화 하기 - 떽떽대는 개발공부

떽이 2021. 2. 24. 19:19

 

 

웹페이지 구현 중 회원가입 부분을 코딩하던 중 암호화 하는 부분이 있어 포스팅 한다.

암호화는 crypto 를 이용하여 단방향 암호화를 해줄 것이다.

클라이언트에서 암호화 해서 서버로 전달하는 방법을 찾았지만 아직까지 소득이 없어.. 서버 쪽에서 암호화 해서 db 에 저장하는 방식으로 진행할 것이다.

아래와 같이 서버단에서 전달받은 parameter 에 암호화를 해주고 그 안에서 db 전달 쿼리를 작성하였다.

const express = require('express');
const router = express();
const db = require('../config/db');
const crypto = require('crypto');

const util = require('util');

router.post('/join_ing', (req,res) => {
    crypto.randomBytes(64, (err, buf) => {
    	//salt는 생성하는 해시값 이외에 추가적인 암호화 값
        const salt = buf.toString('base64');
        console.log('salt :: ', salt);
        //crypto.pbkdf2의 salt 뒤 숫자 파라미터는 임의의 값으로 주어준다.
        crypto.pbkdf2(req.query.cust_pw, salt, 1203947, 64, 'sha512', (err, key) => {
            console.log('password :: ', key.toString('base64')); // 'dWhPkH6c4X1Y71A/DrAHhML3DyKQdEkUOIaSmYCI7xZkD5bLZhPF0dOSs2YZA/Y4B8XNfWd3DHIqR5234RtHzw=='
            
            // 클라이언트로부터 전달받은 params 확인
            console.log(`= = = > req : ${util.inspect(req.query)}`)
            // 쿼리 작성하여 전달
            const sql = 'INSERT INTO `cust_inform` (`cust_no`, `cust_id`, `cust_pw`, `cust_salt`, `cust_name`, `cust_phone`, `cust_email`, `cust_fulladdress`, `cust_extraaddress`, `cust_postcode` ) VALUE ( (SELECT ifnull(max(`cust_no`), 0) + 1 as `cust_no` from `cust_inform` A) , ? , ?, ? , ? , ? , ? , ? , ? , ? )'
            const param = [req.query.cust_id, key.toString('base64'), salt, req.query.cust_name, req.query.cust_phone, req.query.cust_email, req.query.cust_fulladdress, req.query.cust_extraaddress, req.query.cust_postcode]
            db.query(sql, param, (err, data) => {
                if(!err) {
                    res.send(data)
                } else {
                    res.send(err)
                }
            })
        });
    });
})
 
module.exports = router;

 

위와 같이 작성하면 서버로부터 전달 받은 req.query 값은 아래와 같다.

클라이언트로부터 전달받은 pw 는 'qwer1234' 이지만 password :: 쪽을 보면 암호화 되어 알수없는 문자로 변환 되었다.

이제 변환된 비밀번호와 salt 값도 함께 db 에 입력해주면 된다.

 

**중요한 점은 이 변환된 비밀번호와 변환할 때 생성했던 salt 값 모두 db 에 함께 넣어 주어야 한다는 점이다. 나중에 salt 값으로 로그인 확인을 할 수가 있다.

// 비밀번호를 확인할 때는 같은 salt 값, 반복 횟수, 비밀번호 길이, 해시 알고리즘, 인코딩 방식 모두 동일해야 함
crypto.pbkdf2('입력비밀번호', '기존salt', 1203947, 64, 'sha512', (err, key) => {
  console.log(key.toString('base64') === '기존 비밀번호');
});

 

위의 코드를 이용하여 로그인 시 비밀번호를 확인해보자.

클라이언트로부터 위와 같이 id와 비밀번호를 전달 받았다.

 

router.post('/login_ing', (req,res) => {
    console.log(`= = = > req : ${util.inspect(req.query)}`)

    const sql = 'SELECT ifnull(`cust_pw`, NULL) AS `cust_pw`, ifnull(`cust_salt`, NULL) AS `cust_salt` FROM `cust_inform` RIGHT OUTER JOIN (SELECT "") AS `cust_inform` ON `cust_id` = ?'
    const param = [req.query.cust_id]
    db.query(sql, param, (err, data) => {
        if(!err) {
            if(data[0].cust_salt !== null){
                crypto.pbkdf2(req.query.cust_pw, data[0].cust_salt, 1203947, 64, 'sha512', (err, key) => {
                console.log('비밀번호 일치 여부 :: ', key.toString('base64') === data[0].cust_pw);
                // true : 아이디, 비밀번호 일치
                // false : 아이디 일치, 비밀번호 불일치
                res.send({ result : key.toString('base64') === data[0].cust_pw })
                });
            } else {
                // null : 아이디 불일치
                res.send({ result : data[0].cust_salt })
            }
        } else {
            res.send(err)
        }
    })
})

mySQL 에 전달받은 id 값으로 비밀번호와 salt 값을 찾는다.

rowdata 값이 0 일 경우 값이 return 되지 않으므로 outer join 을 사용하였다.

만약 db 에 일치하는 값이 없다면 (등록된 아이디 없음) null 로 받을 것이고, 있다면 salt 값과 비밀번호를 받아와서 비교하여 결과값을 전달한다.