원래 회사에서 사용자 비밀번호 암호화 방식으로 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

1. 회원가입

1) Front-End

// 암호화 관련 기능을 제공하는 자바스크립트 라이브러리
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')
            })
      } 
    },

2) Back-End

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에 아래처럼 회원가입이된다.

💡crypto-js를 이용해서 Front-End와 Back-End에서 각각 암호화하는 이유

Front-end에서 비밀번호를 해시화하는 이유는 클라이언트에서 입력한 원본 비밀번호를 서버로 전송할 때, 원본 비밀번호를 보이지 않게 하기위해서이다. 그러나 이것만으론 보안이 보장되지 않는게 해커가 해시화된 비밀번호를 가로채서 그 값을 그대로 서버에 보낼 수도 있기때문이다. 그러면 해커는 가로챈 비밀번호로 로그인 성공...😭
그래서 서버에서도 비밀번호를 해시화해서 해시화된 비밀번호를 비교해서 로그인 가능여부를 확인하는 게 좋다.

그리고 추가로 Front-end에서 해시화하는 것하고 Back-end에서 해시화하는게 알고리즘? salt? 등의 설정이 좀 다른거 같다. (salt넣고 해보는데 값이 이상하게 나옴😧) 해시화된 결과가 일치하지 않는 문제가 있어서 Front-end와 Back-end는 왠만하면 동일한 설정으로 해시화를 수행하는 것이 좋을 것같다.

2. 로그인

1) Front-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')
            })
        }

2) Back-End

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
        })

    })
});
profile
매일 조금씩 성장하는 개발자

0개의 댓글