Salted password

BenKim·2020년 8월 23일
0

2주프로젝트때 서버쪽을 맡았는데 API쪽에만 신경쓰다가보니 어느날 데이터베이스에 비밀번호가 떡하니 적혀있는것을 보았다.

비밀번호는 관리자여도 알면 안되는 정보이기 때문에 암호화하는 부분을 추가해줬다.

비밀번호를 암호화할때는 해시알고리즘(SHA256, SHA512, RipeMD WHIRLPOOL등)을 통해 알수없는 문자열로 변환시키는데 이것만으로는 안전하지가 않다. 왜냐하면 사용되는 해시알고리즘은 어느정도 정해져있기 때문에 해커도 무작위값을 해시알고리즘에 넣어 그 결과를 비교해서 원본을 알아낼 수 있다.

사람은 비밀번호를 지을때 기억하기위해서라도 알고있는 단어나 의미있는 번호들을 넣을 가능성이 높기 때문에 해커입장에서는 사전에 있는 단어들은 컴퓨터를 이용해서 다 넣어보면 낮지않은 확률로 비밀번호를 알아낼 수 있다.

그래서 필요한 개념이 'salt'라는 개념이다 원본비밀번호에 약간의 문자열만 추가해도 해시결과는 전혀다르게 된다. 그래서 해커가 원본비밀번호를 추측하더라고 그 뒤에 붙여지는 salt값을 모르면 해시결과를 비교할 수 없게 된다.

아래내용은 crypto 모듈과 랜덤으로 생성되는 salt값을 이용해 비밀번호를 암호화한 코드이다.

//회원가입부분
const models = require('../models');
const Users = models.user;
const crypto = require('crypto');
signup: (req, res) => {
  //salt값은 오늘 날짜와 랜덤으로 생성되는 값의 조합으로 만들어져서 db에 저장되게된다.
    let salt = Math.round((new Date().valueOf() * Math.random())) + '';
    let hashPassword = crypto.createHash("sha512").update(req.body.password + salt).digest("hex");
    Users.findOne({ where: { email: req.body.email } })
      .then((user) => {
        if (user) {
          res.status(409).send({ error: '409 Conflict' });
        } else {
          Users.create({
            userName: req.body.username,
            email: req.body.email,
            password: hashPassword,
            salt: salt // salt는 항상변할것이기때문에 지금 생성된값을 db에 저장해둔다.
          })   

로그인 할때 해싱결과를 비교하는 방법은 원본을 비교하는 방법이 아니라 로그인 시도시 입력한 비밀번호에 같은 salt값을 넣어 같은 방식으로 해싱한 뒤 그 결과값을 비교하는 것이다.
어떻게든 비밀번호 원본은 노출이 되지 않기때문에 보안에 좋은 방법이다.

아래는 로그인시 +salt +hashing 된 값을 db에 저장되어있는 해싱된 비밀번호와 비교하는 코드이다.

login: (req, res) => {
    Users.findOne({
      where: { email: req.body.email },
    })
      .then((user) => {
        let salt = user.dataValues.salt;
        let hashPassword = crypto.createHash("sha512").update(req.body.password + salt).digest("hex");
        if (user.dataValues.password === hashPassword) {
          req.session.userId = user.id;
          res.status(200).send({
            id: user.id,
            email: user.email,
            username: user.userName,
          });
        } else {
          res.status(401).send({ error: '401 Unauthorized' });
        }
      })
       .catch(() => res.status(401).send({ error: '401 Unauthorized' }));


참고 : https://starplatina.tistory.com/entry/%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%ED%95%B4%EC%8B%9C%EC%97%90-%EC%86%8C%EA%B8%88%EC%B9%98%EA%B8%B0-%EB%B0%94%EB%A5%B4%EA%B2%8C-%EC%93%B0%EA%B8%B0
profile
연습과 자신감

0개의 댓글