비밀번호 암호화 (pbkdf2, salt, hash, 사용)

2021. 11. 18. 17:10프로그래밍/개인프로젝트

서버 저장값 예시


암호화

사용자의 정보를 받을 때 비밀번호를 직접 받으면 보안에 굉장히 취약하다는 문제가 있다
그래서 입력받은 비밀번호를 해쉬값으로 변경해서 서버에 저장해야한다

해쉬? : 입력값을 일정한 로직에 따라 사람이 알 수 없는 문자로 변환해줌



내생각 : 


hash를 사전에 검색하면 나오는 정의 
1.'#'이라는 기호 
2.'고기와 감자를 잘게 다져 섞어 요리하여 따뜻하게 차려 낸 것' 이라는 요리법  

해시함수에 대한 정의
1.'하나의 주어진 출력에 대하여 이 출력으로 사상시키는 하나의 입력을 찾는 것이 계산적으로 불가능하고, 하나의 주어진 입력에 
대하여  같은 출력으로 사상시키는 또 다른 입력을 찾는 것이 계산적으로 불가능하...'

, 요리에 빗대서 쉽게 생각해보면 고기와 감자를 잘게다져서 섞으면 어떤재료들이 어떤 형태로 들어갔는지 
사람은 알 수 없게 되는것처럼 입력값을 사람이 알 수 없는 문자들로 변환해주는 작업이라고 할 수 있다


 

md5를 사용하는 방법 (사용금지, 단순 공부용)

1. 모듈설치
    npm istall md5
2. server에 md5 로드
    let md5 = require('md5')
      사용자가 입력하는 값을 md5()로 묶어주고 서버와 대조해서 맞으면 로그인

사용예제 :
   md5('입력값')을 출력해보면 단어에 따라 암호화된 문자를 출력해준다
   md5는 단방향 이다 (입력값→해쉬값은 가능, 해쉬값으로 입력값을 알아내는것은 불가능' 
사용상의 문제 : 
   하지만 암호화된 해쉬값을 입력값으로 변환해주는 프로그램을 사용하면 중요 정보가 노출될 수 있으므로 단독으로 사용하면 안된다
   그리고 보안이 뚫렸으므로 md5는 암호화로서의 가치가 없어졌다고 할 수 있다

해쉬값변환을 통한 정보노출에 대한 해결책 salt

 


랜덤생성되는 암호화 키값인 salt를 사용자가 입력한 비밀번호에 더해줘서 암호화된 해쉬값을 만들면 
해쉬값 변환 프로그램을 통해 알 수가 없다

서버에 비밀번호+salt를 넣어두고 입력값으로 md5(pwd+salt)를 하면 해결

salt값을 생성해주는 pbkdf2를 사용하도록 해보자



pbkdf2 (Password-Based Key Derivation Function 2)

 

pbkdf2는 키 스트레칭 기법 (중첩암호화기법)을 사용하기 위해 만들어진 함수이다
사용방법
1. 모듈설치
    npm install pbkdf2-password
2. server.js에 코드작성
    let bkfd2Password = require('pbkdf2-password')
    let hasher = bkfd2Password();
    hasher({password:'비밀번호'}, function(err, pass, salt, hash){
        console.log(err, pass, salt, hash);
        //콘솔로그로 찍으면  err = undefined, pass:입력한비밀번호값, salt: 랜덤생성, hash: 입력비밀번호+salt값에 대한 해쉬값 을 출력해준다
    })

내 프로그램에 적용?

회원가입부분과 로그인 부분에 코드를 추가하였다


회원가입

	app.post('/signupResult',function(req, res){
        hasher({password: req.body.password}, function(err, pass, salt, hash){
            // console.log(err, pass, salt, hash);
            // err = undefined, pass:입력한비밀번호값, salt: 랜덤번호생성, hash: 입력비밀번호+salt값에 대한 해쉬값 을 출력해준다

            db.collection('users').insertOne({ id : req.body.loginId, hashPassword : hash, saltPassword : salt, email : req.body.email, number : req.body.number, gender : req.body.gender,birthday : req.body.birthday, }, function(err, result){
                console.log('db save')
            })
            res.send("<script>alert('회원가입하셨습니다.');location.href='/login';</script>");
        })    
    })

로그인

	app.get('/login',function(req, res){
        let fmsg = req.flash(); // 로그인 실패시 출려되는 플래시메세지
        // console.log(fmsg); // 아이디가 없는지 , 비밀번호가 틀렸는지 검사해서 작성해놓은 message를 출력해준다
        let feedback= '';
        if(fmsg.error){
            feedback = fmsg.error[0]
        }

        res.render('login.ejs', {loginFeedback : feedback})
    });
    //passport라이브러리 사용
    app.post('/login', passport.authenticate('local', { // 로컬방식으로 인증
        failureRedirect : '/fail' ,
        failureFlash : true //실패시 /fail페이지로 이동시키기
        }), function(req, res){
            res.redirect('/') // 성공시 redirect해서 홈페이지로 보내주기
        });
        app.get('/fail', function(req, res){ // /fail로 접속시 처리할 코드 (alert창을 띄우고 로그인으로 리다이렉트)
            res.redirect('/login')
        })

    //new LocalStrategy인증방식
    //LocalStrategy( { 설정 }, function(){ 아이디비번 검사하는 코드 } )
    passport.use(new LocalStrategy({
        usernameField: 'loginId', //사용자가 제출한 id가 어디 적혔는지(input의 name 속성값)
        passwordField: 'loginPassword', //사용자가 제출한 pw가 어디 적혔는지(input의 name 속성값)
        session: true,  //로그인 후 세션을 저장할지 ?
        passReqToCallback: false, //사용자가 입력한 id,pw외에 다른정보도 검증해보고 싶으면

    }, function (inputId, inputPw, done) {
    // console.log(입력한아이디, 입력한비번); //-> 사용자가 입력한 id/pw가 콘솔로그로 출력됨
        db.collection('users').findOne({ id: inputId }, function (err, user) {
            //입력한 id에 대한 서버 정보를 결과에 담아옴
            if (err) return done(err) //에러처리문법

            //done은 3개의 파라미터를 가질수 있음, 1: 서버에러, 2: 성공시 사용자db, 3:에러메시지
            if (!user) {
                console.log('아이디가없어요')
                return done(null, false, { message: 'incorrect id' })

            }
            if(user){
                console.log('유저있어요')
                hasher({password:inputPw, salt: user.saltPassword}, function(err, pass, salt, hash){ //입력비밀번호와 유저의 salt를 가져와서 hash로 만들고
                    // console.log(err, pass, salt, hash);
                    // err = undefined, pass:입력한비밀번호값, salt: 랜덤번호생성, hash: 입력비밀번호+salt값에 대한 해쉬값 을 출력해준다
                    
                    // 입력비밀번호와 유저의 salt를 가져와서 만든 hash와 서버의 해쉬값과 일치하다면  로그인성공
                    if (hash == user.hashPassword) {
                        return done(null, user)
                    } else {
                        console.log('비밀번호가 틀렸어요');
                        return done(null, false, { message: 'incorrect password' })
                    }
                })
            }
        })
    }));
 

출처 : 생활코딩 암호화 강좌 + 프로젝트 적용을 위한 개인 공부