비밀번호 보안은 현대 정보 시스템의 핵심 요소 중 하나이다. 비밀번호 해시는 사용자 비밀번호를 안전하게 보호하기 위한 필수적인 방법으로, 해커로부터 중요한 정보를 지키는 역할을 한다. 이 글에서는 비밀번호 해시를 사용하지 않을 경우 발생할 수 있는 문제점, 비밀번호 해시의 기본 원리, 그리고 bcryptjs 라이브러리를 사용하여 비밀번호를 안전하게 해시화하고 검증하는 방법을 다룰 것이다.
목차
1. 해시 기본 원리
2. 해시를 사용하지 않을 경우 생길 수 있는 일
3. bcryptjs 라이브러리를 활용한 암호화 방법
1. 해시 기본 원리
해시는 비밀번호를 안전하게 저장하기 위한 필수적인 기술이다. 해시 함수는 입력 값을 고정된 크기의 암호화된 출력 값으로 변환하는 일방향 함수로, 비밀번호의 보안을 강화한다.
(1) 해시 함수의 정의
해시 함수는 임의의 길이를 가진 입력 데이터를 고정된 길이의 출력 데이터로 변환하는 함수이다. 이 출력 데이터는 해시 값 또는 다이제스트(digest)라고 불린다. 해시 함수의 중요한 특징은 일방향성이다. 즉, 해시 값을 통해 원래 입력 값을 역으로 추정하는 것이 거의 불가능하다.
(2) 비밀번호 해시의 필요성
비밀번호를 해시화하여 저장하는 이유는 데이터베이스가 해킹 당했을 때, 비밀번호가 노출되지 않도록 하기 위함이다. 평문 비밀번호를 저장하면, 데이터베이스가 유출될 경우 모든 사용자의 비밀번호가 노출된다. 그러나 해시된 비밀번호는 원래의 비밀번호로 되돌릴 수 없기 때문에 보안이 강화된다.
(3) 솔트(Salt)의 사용
해시 함수의 한계 중 하나는 동일한 입력 값에 대해 항상 동일한 해시 값을 생성한다는 점이다. 이를 해결하기 위해 솔트(Salt)를 사용한다. 솔트는 해시 함수에 추가되는 무작위 데이터로, 각 비밀번호마다 고유한 솔트를 생성하여 해시화 과정에 포함시킨다. 이를 통해 동일한 비밀번호도 다른 해시 값을 가지게 되어, 무차별 대입 공격(브루트 포스 공격)이나 사전 공격(Dictionary attack)을 방지할 수 있다.
(4) 해시 충돌
해시 충돌이란 서로 다른 입력 값이 동일한 해시 값을 생성하는 경우를 말한다. 이는 해시 함수의 본질적인 한계이지만, 강력한 해시 함수는 충돌 발생 가능성을 극도로 낮추어 보안성을 높인다. 비밀번호 해시에서는 충돌을 피하기 위해 SHA-256, bcrypt, Argon2와 같은 강력한 해시 함수를 사용한다.
(5) 해시 함수의 종류
- MD5 : 과거에 널리 사용되었으나, 현재는 보안 취약점으로 인해 사용이 권장되지 않는다.
- SHA-1 : SHA-0의 후속 버전이지만, 현재는 역시 취약점이 발견되어 사용이 권장되지 않는다.
- SHA-256 : 비교적 안전한 해시 함수로, 많은 보안 시스템에서 사용된다.
- bcrypt : 비밀번호 해시에 특화된 함수로, 계산 비용이 높아 무차별 대입 공격을 어렵게 한다.
- Argon2 : 현대적인 해시 함수로, 높은 보안성과 유연성을 제공한다. 2015년 비밀번호 해싱 대회에서 우승했다.
2. 해시를 사용하지 않을 경우 생길 수 있는 일
해시를 사용하지 않을 경우, 여러 가지 심각한 보안 문제에 직면할 수 있다. 이러한 문제는 사용자 정보의 유출, 계정 도용, 시스템의 신뢰도 저하 등 다양한 형태로 나타날 수 있다.
(1) 사용자 정보 유출
가장 직접적인 위험은 사용자 정보가 유출될 가능성이다. 만약 비밀번호가 평문으로 저장된다면, 해커가 데이터베이스에 접근했을 때 모든 비밀번호를 쉽게 알아낼 수 있다. 이는 데이터 유출 사고가 발생했을 때 수백만 명의 사용자 정보가 단순한 텍스트 파일로 공개될 수 있음을 의미한다.
(2) 계정 도용
해킹을 통해 유출된 비밀번호가 평문으로 저장되어 있다면, 공격자는 이를 사용해 해당 사용자의 다른 계정에도 접근할 수 있다. 많은 사용자가 동일한 비밀번호를 여러 서비스에서 재사용하기 때문에, 하나의 비밀번호 유출이 여러 계정의 도용으로 이어질 수 있다. 예를 들어, 이메일, 금융 서비스, 소셜 미디어 계정 등이 도용될 위험이 있다.
(3) 시스템 신뢰도 저하
비밀번호를 평문으로 저장하는 시스템은 사용자들로부터 신뢰를 잃을 수 있다. 만약 사용자가 자신들의 비밀번호가 안전하게 보호되지 않고 있다는 사실을 알게 된다면, 해당 시스템에 대한 신뢰도가 급격히 저하될 것이다. 이는 사용자 이탈, 서비스 이용 감소 등으로 이어질 수 있으며, 기업의 이미지와 매출에도 부정적인 영향을 미친다.
(4) 법적 문제
비밀번호를 안전하게 저장하지 않는 것은 법적 문제를 야기할 수 있다. 많은 국가에서는 사용자 데이터를 보호하기 위한 법률을 제정하고 있으며, 이를 위반할 경우 막대한 벌금과 법적 제재를 받을 수 있다. 예를 들어, 유럽연합의 일반 데이터 보호 규칙(GDPR)에서는 데이터 보호와 관련한 엄격한 규정을 두고 있다.
3. bcryptjs 라이브러리를 활용한 암호화 방법
bcryptjs는 자바스크립트 환경에서 비밀번호 해시화를 구현하기 위해 널리 사용되는 라이브러리이다. bcrypt 알고리즘을 기반으로 하며, 비밀번호 해시화를 안전하고 쉽게 처리할 수 있도록 도와준다.(1) bcryptjs 설치
bcryptjs를 사용하기 위해서는 먼저 npm(Node Package Manager)을 통해 라이브러리를 설치해야 한다. 다음 명령어를 사용하여 bcryptjs를 설치할 수 있다.
npm install bcryptjs
설치가 완료되면, 프로젝트 내에서 bcryptjs 라이브러리를 사용할 준비가 완료된다.
(2) 비밀번호 해시 생성
비밀번호를 해시화하기 위해서는 bcryptjs의 hash 메서드를 사용한다. 이 메서드는 비밀번호와 솔트를 받아 해시 값을 생성한다.
const bcrypt = require('bcryptjs');
const plainPassword = 'mySecurePassword';
const saltRounds = 10;
bcrypt.hash(plainPassword, saltRounds, function(err, hash) {
if (err) throw err;
console.log('Hashed Password:', hash);
});
위 코드에서는 plainPassword 변수에 저장된 비밀번호를 해시화하고, 해시 값은 콜백 함수 내에서 처리된다. saltRounds는 해시 생성 시 반복되는 횟수를 의미하며, 보통 10에서 12 사이의 값을 사용한다.
(3) 비밀번호 검증
사용자가 입력한 비밀번호가 저장된 해시 값과 일치하는지 확인하려면 bcryptjs의 compare 메서드를 사용한다. 이 메서드는 입력된 비밀번호와 저장된 해시 값을 비교하여 일치 여부를 반환한다.
const bcrypt = require('bcrypt');
const inputPassword = 'mySecurePassword';
const storedHash = '$2a$10$EIXr.pTgmn5aYfTIpLZmUuJt7w2hQF.vI9Oq5Imw6bg.BaK3OklF6'; // 예제 해시 값
bcrypt.compare(inputPassword, storedHash, function(err, isMatch) {
if (err) throw err;
if (isMatch) {
console.log('Password is correct');
} else {
console.log('Password is incorrect');
}
});
위 코드에서는 inputPassword 변수에 저장된 사용자의 입력 비밀번호를 storedHash 변수에 저장된 해시 값과 비교하여 일치 여부를 확인한다.
(4) 솔트 생성
솔트를 별도로 생성하여 해시화 과정에 사용하는 방법도 있다.
// 솔트 생성
const salt = bcrypt.genSaltSync(saltRounds);
console.log('Generated Salt:', salt);
// 솔트를 사용한 해시 생성
const hashWithSalt = bcrypt.hashSync(plainPassword, salt);
console.log('Hashed Password with Salt:', hashWithSalt);