본문 바로가기
Node.js

암호화 (+crypto)

by 슈크림 붕어빵 2024. 1. 5.
암호화(Encryption) : 평문 -> 암호문 변환
복호화(Decryption) : 암호문 -> 평문 변환

 

  • 양방향 암호화
    • 개인키 암호화 : 암호화 키 == 복호화 키
      • Stream : 문자열 길이의 Stream을 생성해 bit단위로 암호화
      • Block : 하나의 블럭 단위로 암호화
    • 공개키 암호화 : 암호화 키 != 복호화 키
  • 단방향 암호화 : 암호화는 가능 / 복호화는 불가능!
    • Hash
      : 어떤 입력이든 고정된 길이의 문자열로 반환하여 암호화
        비밀번호 암호화에 주로 사용됨 / 양방향 암호화보다 빠름
        (Hash에 사용되는 Hash알고리즘은 공개되어 있어서 보안에 취약
              -> Salt를 넣거나, 여러번 Hash함수를 돌려서 강화해서 사용!)

password를 숨기는 이유는 뭘까? db가 해킹당할까봐?

만일 db가 털렸을 때, salt와 암호화된 것을 털리면 원래 비번을 복호화할 수는 없으니 안전함.

 

암호화해보기 - crypto 내장함수 사용

const crypto = require("crypto"); //내장 암호화 모듈

//비밀번호 암호화
  const salt = crypto.randomBytes(64).toString("base64");
  const hashPassword = crypto
    .pbkdf2Sync(password, salt, 10000, 64, "sha512")
    .toString("base64");

randomBytes: 매개변수로 들어오는 숫자를 가지고 랜덤바이트를 만들어 준다. 64의 길이로 만들어줌

toString: 다시 string으로 만들어준다.base64방식의 문자열로. (기본적으로 문자열은 base64를 사용한다.)

pbkdf2Sync (password-Based Key Derivation Function 2): 이 매개변수로 

(비밀번호, 솔트값, 반복 횟수, 출력 byte, 해시 알고리즘, callback) 순이다.

단방향 - 로직

1. 회원가입시 암호화된 비밀번호와, salt값 저장

2. 로그인 시 입력된 비밀번호를 salt값을 통해 비밀번호 암호화하여 비교

회원가입

const join = (req, res) => {
  const { email, name, password } = req.body;

  //비밀번호 암호화
  const salt = crypto.randomBytes(64).toString("base64");
  const hashPassword = crypto
    .pbkdf2Sync(password, salt, 10000, 10, "sha512")
    .toString("base64");

  let sql = "INSERT INTO users (email,name,password,salt) VALUES (?,?,?,?)";
  let values = [email, name, hashPassword, salt];
  //암호화된 비밀번호와 salt값을 같이 db에 저장
  conn.query(sql, values, (err, results) => {
    if (err) {
      console.log(err);
      return res.status(StatusCodes.BAD_REQUEST).end();
    }
    return res.status(StatusCodes.CREATED).json(results);
  });
};

사용한 모듈 - crypto (암호화), jsonwebtoken (토큰 발급)

 

로그인

const login = (req, res) => {
  const { email, password } = req.body;

  let sql = "SELECT * FROM users WHERE email = ?";
  let values = [email];
  conn.query(sql, values, (err, results) => {
    if (err) {
      console.log(err);
      return res.status(StatusCodes.BAD_REQUEST).end();
    }
    const loginUser = results[0];

    //암호화
    const salt = loginUser.salt;
    const hashPassword = crypto
      .pbkdf2Sync(password, salt, 10000, 10, "sha512")
      .toString("base64");

    //해시 password 비교
    if (loginUser && loginUser.password == hashPassword) {
      //토큰 발급
      const token = jwt.sign(
        {
          email: loginUser.email,
          name: loginUser.name,
        },
        process.env.PRIVATE_KEY,
        {
          expiresIn: "15m",
          issuer: "sungeun",
        }
      );
      console.log(token);

      res.cookie("token", token, {
        httpOnly: true,
      });
      return res
        .status(StatusCodes.OK)
        .json({ message: `${loginUser.name}님 환영합니다.` });
    } else {
      return res.status(StatusCodes.UNAUTHORIZED).json({
        message: "id 및 pw를 확인해주세요.",
      });
    }
  });
};