2023.05.16 bcrypt

이무헌·2023년 7월 21일
0

node.JS

목록 보기
6/10
post-thumbnail

1.bcrypt를 이용한 비밀번호 암호화

  • 전체 코드
    const { useSelect, userInsert } = require("../models");
    const bcrypt = require("bcrypt");
    
    // 모듈 추가 암호화 모듈
    // 강력한 암호화를 지원하고 쉽게 사용 가능하다.
    
    // bcrypt 모듈 사용하자
    // npm i bcrypt
    // $2a$[cost]$[salt][hash]
    
    // $2a$:사용 알고리즘 고정
    // [cost]:키 스트레칭 횟수 입력한 값이 2의 ^으로 들어간다. 10을 입력하면 2^10 많이 사용하는 횟수가
    // 기본적으로 10을 많이 사용함. 이거보다 높이면 많이 느려질 수 있다.
    
    // [salt]:인코딩된 salt값 문자열의 일부분을 salt값으로 사용한다 알고리즘에서
    // [hash]:비밀번호와 salt값을 합하고 해시해서 인코딩된 값
    
    // bcrypt:보안에 집착하기로 유명한 OpenBSD에 사용
    // 반복횟수를 늘려 연산속도를 늦출 수 있기 때문에 연산능력이 증가해도
    // 공격에 대비 할 수 있다.
    // 암호화된 문자열 중에서 일부분을 salt값으로 사용하고 있다.
    const createHash = (password) => {
      return new Promise((res, rej) => {
        // hash 메서드로 해시값을 만들어 줄 수 있다.
        bcrypt.hash(password, 10, (err, data) => {
          if (err) {
            rej(err);
          } else {
            res(data);
          }
        });
      });
    };
    
    const compare = (password, hash) => {
      return new Promise((res, rej) => {
        // compare 메서드를 사용해서 문자열과 해시값을 전달해주고 매개변수로
        // 검증 결과를 확인한다.
        bcrypt.compare(password, hash, (err, same) => {
          res(same);
        });
      });
    };
    
    exports.SignUp = async (req, res) => {
      const { user_id, user_pw } = req.body;
      try {
        const hash = await createHash(user_pw);
        await userInsert(user_id, hash);
        res.redirect("/login");
      } catch (error) {
        console.log("SignUp in userControllers");
      }
    };
    
    exports.Login = async (req, res) => {
      const { user_id, user_pw } = req.body;
      try {
        const data = await useSelect(user_id);
        if (!data?.user_id) {
          return res.send("id 없음");
        }
        const compare_pw = await compare(user_pw, data.user_pw);
        if (!compare_pw) {
          return res.send("비밀번호가 틀렸습니다.");
        }
        res.send("로그인 됨");
      } catch (error) {
        console.log("Login inuserController", error);
      }
    };
  • bcrypt를 이용한 해쉬 생성
    const createHash = (password) => {
      return new Promise((res, rej) => {
        // hash 메서드로 해시값을 만들어 줄 수 있다.
        bcrypt.hash(password, 10, (err, data) => {
          if (err) {
            rej(err);
          } else {
            res(data);
          }
        });
      });
    };
    • 해쉬를 만드는 함수이다.
    • 해쉬를 만드는 일은 오래걸리기 때문에 비동기적으로 처리할 필요가있다.
    • 따라서 promise를 반환한다.
    • hash의 첫번째 매개변수는 해쉬화 할 비밀번호, 두번째 매개변수는 키 스트레칭 횟수이다.
    • 만들어진 hash의 형식이다. m8opZ.png
      • salt는 기존 비밀번호에 임의의 데이터를 더해 해쉬화 하여 보안을 강화 하는 것이다.
      • 처음 algorithm은 bcrypt에서 지정한 알고리즘의 종류다.
  • 입력한 비밀번호와 mysql에 저장한 hash와 비교
    const compare = (password, hash) => {
      return new Promise((res, rej) => {
        // compare 메서드를 사용해서 문자열과 해시값을 전달해주고 매개변수로
        // 검증 결과를 확인한다.
        bcrypt.compare(password, hash, (err, same) => {
          res(same);
        });
      });
    };
    • bcrypt.compare 함수를 이용해서 편하게 비교할 수 있다.
    • 첫번째 매개변수는 유저가 입력한 비밀번호,두번째 매개변수는 mysql에 저장된 해쉬화된 유저의 비밀번호이다.
    • 판별 여부에 따라 다르면 same=false,같은면 same=true가 할당된다.
  • 회원가입에 적용
    exports.SignUp = async (req, res) => {
      const { user_id, user_pw } = req.body;
      try {
        const hash = await createHash(user_pw);
        await userInsert(user_id, hash);
        res.redirect("/login");
      } catch (error) {
        console.log("SignUp in userControllers");
      }
    };
    • promise가 반환되기 때문에 당연히 await 키워드를 붙였고, 해당 값을 비밀번호 매개변수에 넣는다.
  • 로그인에 적용
    exports.Login = async (req, res) => {
      const { user_id, user_pw } = req.body;
      try {
        const data = await useSelect(user_id);
        if (!data?.user_id) {
          return res.send("id 없음");
        }
        const compare_pw = await compare(user_pw, data.user_pw);
        if (!compare_pw) {
          return res.send("비밀번호가 틀렸습니다.");
        }
        res.send("로그인 됨");
      } catch (error) {
        console.log("Login inuserController", error);
      }
    };
    • data.user_pw는 해쉬화된 비밀번호이므로 compare의 두번째 매개변수로 넣는다.
    • 반환 값 (resolve)는 boolean타입 이므로 false일 경우 비밀번호가 틀렸다고 전달한다.

2.느낀점

💡 예전에 spring security를 쓰면서 해쉬에대해 짤막하게 독학했던 기억이 난다. 스프링에 비해서 훨씬 쉬운 방법으로 암호화를 하고, 비교또한 간편한 것 같다. 라이브러리에 의해 필수요소들이 간편화 되어가고 있지만 그 원리를 정확히 이해하는것이 중요한 것 같다. 특히 salt부분은 더 정확한 이해를 할 필요가 있다.
profile
개발당시에 직면한 이슈를 정리하는 곳

1개의 댓글

comment-user-thumbnail
2023년 7월 21일

항상 좋은 글 감사합니다.

답글 달기