bcrypt
는 비밀번호를 안전하게 저장하기 위해 설계된 해시 함수로, 무작위 솔트와 반복적인 해시 과정을 통해 비밀번호를 보호한다. 이를 통해 원본 비밀번호를 저장하지 않고도 안전한 인증을 가능하게 하며, 무작위 대입 공격에 강한 보안을 제공한다.
단방향 해시 함수: bcrypt로 해시된 비밀번호는 역으로 복구할 수 없다. 해시된 비밀번호는 원본 비밀번호를 알 수 없게 단방향으로 변환된다.
솔트 사용: bcrypt는 랜덤한 "솔트(salt)" 값을 추가하여 해시를 생성한다. 같은 비밀번호라도 무작위 솔트를 추가해 결과가 달라지며, 사전 공격이나 무작위 대입 공격(Brute Force)에 대한 보안을 강화한다.
비용 인자: bcrypt는 해싱을 수행할 때 "비용 인자(cost factor)"를 설정할 수 있다. 해시 연산을 여러 번 반복하는 비용 인자 설정을 통해, 시스템 성능에 맞춰 보안 수준을 조정할 수 있다. 일반적으로 비용 인자는 10에서 12 사이로 설정된다.
느린 해시: bcrypt는 의도적으로 느리게 동작하도록 설계되어 있다. 이를 통해 무작위 대입 공격과 같은 해킹 시도를 어렵게 만든다.
비밀번호 해시: 사용자가 회원가입할 때 입력한 비밀번호를 bcrypt를 사용하여 해시한 후 데이터베이스에 저장한다. 이를 통해 원래 비밀번호는 서버에 저장되지 않고, 해시된 값만 데이터베이스에 저장된다.
비밀번호 검증: 사용자가 로그인할 때, 입력한 비밀번호와 데이터베이스에 저장된 해시된 비밀번호를 bcrypt를 사용해 비교한다. 이 과정에서 bcrypt의 compare 함수를 사용하여 일치 여부를 확인한다.
import { BeforeInsert, Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
import * as bcrypt from 'bcrypt';
@Entity('user')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
username: string;
@Column()
password: string;
@BeforeInsert()
async hashPassword() {
const saltRounds = 10;
this.password = await bcrypt.hash(this.password, saltRounds);
}
}
async comparePassword(plainPassword: string, hashedPassword: string): Promise<boolean> {
return await bcrypt.compare(plainPassword, hashedPassword);
}
bcrypt는 비밀번호를 해시하거나 검증할 때 시간이 오래 걸리는 연산을 수행한다. 여러 번의 반복적인 연산을 통해 암호화된 해시 값을 생성하기 때문에, 이 과정을 동기적으로 처리하면 서버가 다른 요청을 처리할 수 없게 되는 블로킹(blocking) 상태가 발생할 수 있다. 비동기 처리를 사용하면 bcrypt가 비밀번호를 해시하거나 검증하는 동안 다른 작업을 진행할 수 있기 때문에 서버의 성능이 크게 개선된다.