많은 서비스들이 사용자의 정보를 서버에 저장합니다. 가장 대표적인 사용자의 정보라면 아이디와 비밀번호를 들 수 있습니다. 오늘은 사용자의 비밀번호를 어떻게 암호화해서 서버에 전달하는지에 대해서 작성하려고 합니다. 비밀번호를 암호화할 때 사용한 패키지는 bcrypt
입니다.
보통 비밀번호를 저장할 때는 해시 함수를 통해 암호화된 비밀번호를 만들어 전송하게 됩니다. 예를 들어 어떤 사용자가 비밀번호를 abcd
로 변경했다면 이 텍스트를 그대로 전송할 경우 보안의 위험이 크기 때문에 권장되지 않습니다. 때문에 사용자가 입력한 텍스트를 암호화해, 이 암호화된 메세지로는 원본 텍스트를 구할 수 없도록 하고 있습니다.
대부분의 웹사이트에서는 암호화를 하기 위한 해시 알고리즘으로 SHA-256
을 사용합니다. 256비트로 구성되어있고 64자리의 문자열을 반환해주는 방식입니다. 이 방식을 사용하면 암호화된 문자열만으로는 원본 텍스트를 유추할 수 없는 단방향 암호화 방식입니다. 서버는 이렇게 생성된 암호화된 메세지 즉 다이제스트끼리의 비교를 통해 같은 다이제스트라면 로그인을 승인하는 형태로 서비스를 이용할 수 있도록 합니다.
그러나 단방향 해시 함수가 갖는 문제점도 있습니다. 동일한 텍스트가 언제나 동일한 값을 반환한다면 웹사이트를 공격하려는 사람이 최대한 많은 다이제스트를 확보한 뒤 원본 텍스트를 찾아내는 것이 가능하기 때문입니다. 이러한 문제점을 보완하기 위해 '솔팅'이라는 임의의 문자열을 추가하는 방법을 사용하게 됩니다.
솔트는 단방향 해시 함수에서 다이제스트를 생성할 때 추가하는 바이트 단위 임의의 문자열입니다. 이 방법을 사용하면 웹사이트를 공격하려는 사람이 원본 텍스트의 다이제스트를 알게 되더라도 솔팅된 다이제스트로 비밀번호가 일치하는지 여부를 확인하기 어렵습니다. 사용자별로 다른 솔트를 사용하게 된다면 더더욱 일치 여부를 확인하기 어렵게 됩니다.
bcrypt
bcrypt
는 비밀번호 저장을 목적으로 설계된 해시 매커니즘 중 하나입니다. 위에서 설명한 솔팅 방식을 사용하기 때문에 보안에 유리한 방식이라고 볼 수 있습니다. 이 패키지를 사용하면 생성한 솔트와 함께 비밀번호를 해시해 서버에 전송하는 것을 쉽게 만들어줍니다.
위 화면은 네이버의 비밀번호 변경 화면입니다. 많은 웹사이트들이 네이버처럼 현재 비밀번호와 새 비밀번호를 입력하도록 권장하고 있습니다. bcrypt
를 사용해 기존 비밀번호와의 비교, 그리고 새 해시 비밀번호를 생성하는 과정을 다음과 같이 작성할 수 있습니다. 저는 디비에서 솔트를 받아오고, 새로운 해시 비밀번호를 전송하는 형태로 구현했습니다.
const info = req.body;
const hashedOldPassword = bcrypt.hashSync(oldPass, info.salt);
if (hashedOldPassword === info.hasHashedPassword) {
const newSalt = bcrypt.genSaltSync(10);
const hashedNewPassword = bcrypt.hashSync(info.newPass, newSalt);
// 여기까지의 작업이 끝나면 새로운 솔트와 새로운 해시 비밀번호를 서버로 전송하면 됩니다.
}
패키지를 사용하면 별로 어렵지 않게 비밀번호 암호화를 구현할 수 있습니다. 처음 비밀번호 암호화를 구현할 때 해시한 비밀번호를 보내야 한다는 이야기를 듣고 당황했던 기억이 나네요ㅎㅎ 사실 구현 자체는 어렵지 않은데 왜 이런 방식으로 해야하는지 이해하는데 더 많은 시간이 걸렸던 것 같습니다. 기록을 남기기 위해 즐겨찾기 했던 블로그들을 찾아서 읽으면서 좀 더 이해할 수 있게 된 것 같습니다.