[Youtube 클론코딩] 비밀번호 변경, bcypt의 원리

2022. 11. 23. 18:42공부/Javascript

비밀번호 변경은 다음과 같은 로직을 따른다.

1. 기존 비밀번호, 새로운 비밀번호, 확인용 비밀번호를 입력받는다.

2. 기존 비밀번호가 데이터베이스에 존재하는 비밀번호와 일치하는지를 확인한다.

3. 새로운 비밀번호와 확인용 비밀번호가 서로 같은지 판단한다.

4. 2, 3번을 통과한 경우 새로운 비밀번호를 해시하여 DB에 저장한다.

 

1. 비밀번호 변경 페이지 제작

라우터 설정

이미 로그인 한 사람만 들어올 수 있도록 접근제어 미들웨어를 넣어서 설정해준다.

페이지 제작

패스워드 변경 페이지를 제작한다.

현재 views 를 보면 user용 페이지와 video 용 페이지들이 나뉘어 있는 것을 볼 수 있는데,

이것들을 정리하고 싶으면 따로 users 폴더와 videos 폴더로 옮겨준 뒤 경로 수정들을 해주면 된다.

 

extends 에서 base에 접근할 때 상대 경로로 ../ 를 넣어주는 것을 잊지 말자.

컨트롤러 설정

비밀번호를 설정한 뒤에는 다시 로그인 할 수 있도록 바로 로그아웃으로 이동시켜주는 게 직관적일 것 같다.

 

이제 위에 썼던 2번 3번 과정을 수행하자

2. 기존 비밀번호가 데이터베이스에 존재하는 비밀번호와 일치하는지를 확인한다.
3. 새로운 비밀번호와 확인용 비밀번호가 서로 같은지 판단한다.

비밀번호 일치 확인

2번을 수행하려면 2가지 방법이 떠오른다.

1. 세션에 유저 정보가 존재하므로 세션의 유저 정보를 가져와 비교한다.

2. DB에서 유저 정보를 가져와서 둘을 비교한다.

속도 측면에서 보면 이미 가지고 있는 정보로 비교하는 1번의 방법이 우수해보이지만

우리가 비밀번호를 변경하게 되면 세션 정보 또한 업데이트를 해주어야 하는 번거로움이 존재하고

무엇보다 비밀번호를 변경하려면 어차피 DB에서 유저 정보를 가져와서 save 해야 하므로 2번의 방법이 적절하다.

bcypt.compare를 이용해 비교해준 뒤, 만약 같지 않으면 에러 메세지와 함께 다시 비밀번호 변경 페이지를 렌더링한다.

비밀번호 유효성 확인

3번 로직은 조건문 하나로 쉽게 해결 가능하다.

비밀번호 해싱

우리는 앞서 회원가입 기능을 구현할 때 DB의 hook을 이용해 비밀번호를 해싱하는 과정을 만들었다.

따라서 user DB에서 가져온 유저 객체에서 save() 메소드를 실행하면 자동으로 해시를 진행할 것이다.

세션에도 유저 객체가 존재하는데 왜 굳이 새로운 객체를 DB에서 가져오는가?

그 이유는, 세션에 존재하는 유저 객체는 이전에 user에서 가져온 객체를 복사한 객체이기 때문이다.
실제로 console.dir를 이용해 둘을 비교해서 찍어보면 다음과 같다
위의 model 객체가 실제 user DB에서 가져온 model 객체이고 아래가 세션의 객체이다.
따라서 세션의 객체에는 save()와 같은 DB model의 메소드를 사용할 수 없다.

 

정리하자면, 

1. 어차피 DB에 저장해야하고, save() 메소드는 DB의 model 객체에서만 사용할 수 있다.

2. 세션의 유저 비밀번호를 이용해 비교해야 하면 세션도 새로 업데이트 해줘야 하므로 그냥 DB 정보를 가져오는게 쉽다.

3. 비밀번호를 변경하면 세션 또한 업데이트해 줄 필요 없이 로그아웃하여 세션을 파기한다.

 

말은 거창하지만 실상은 그냥 user model 객체의 password를 새로 입력한 password로 변경한 뒤 save()하면 된다.

bcypt 원리

나는 비밀번호 확인 로직에서 한가지 궁금증이 생겼다.

bcypt.compare()라는 메소드가 있어서 편리하게 사용할 수 있지만 만약 없었다면 어떻게 비교를 했어야 할까?

 

가설

해시 함수는 결정적 함수이므로 넣어준 값이 동일하다면 결과 값이 동일하다.

따라서 해시를 동일하게 돌려보고 나온 결과를 비교해보면 될 것이다.

 

그러나  bcypt에서 bcypt.hash()를 이용해 같은 문자열을 해시해보면 항상 다른 값이 나온다.

이전 비밀번호와 새 비밀번호가 동일하게 12345 이지만, password에 저장된 값은 서로 다른 것을 볼 수 있다.

 

그 이유는 bcypt는 hash()를 실행할 때 salt라고 하여 일종의 보안 문자열을 랜덤하게 집어넣어 주기 때문이다.

출처: bcypt npm

즉, 저 salt와 기존 문자열을 조합하여 나오는 결과가 hash-value인 것이다.

 

따라서 bcypt.compare()는 해시 문자열 앞부분의 정보들(알고리즘, cost, salt) 정보로 주어진 문자열을 똑같이 해싱하는 방식으로 비교작업을 수행하게 된다.