# NestJS/Typescript : Bcrypt 모듈을 통한 비밀번호 암호화

- Author: @laetipark
- Published: 2023-11-01
- Updated: 2023-11-01
- Source: http://blex.me/@laetipark/nestjstypescript-bcrypt-%EB%AA%A8%EB%93%88%EC%9D%84-%ED%86%B5%ED%95%9C-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%95%94%ED%98%B8%ED%99%94
- Tags: nodejs, 해시, nestjs, 암호화, bcrypt, typescript

---

# 단방향 암호화

- **한쪽 방향으로 암호화**하는 것을 의미
- `암호화`는 가능하지만 `복호화`는 불가능 함
    - **양방향 암호화** : 암호화된 데이터를 복호화 할 수 있으며, 대칭키, 공개키 암호화 알고리즘 등이 있다.
- `해시(Hash) 함수`와 `MD5`, `SHA` 등의 알고리즘이 있다.

# Bcrypt

- `Blowfish 암호`에 기반을 둔 암호화 해시 함수
- `Niels Provos`와 `David Mazières`가 설계
- `단방향 해시 함수의 한계점`을 극복하기 위해 설계

### 해시 함수의 한계점

![](https://static.blex.me/images/content/2023/11/1/202311123_8ZTalzW0XQ5GT7vFxFxO.png)
- **레인보우 테이블** : 동일한 메시지는 동일한 다이제스트(digest)를 갖는 테이블로, 일반적인 해시 함수의 특징
- **무차별 대입 공격**(Brute-Force Attack)에 취약
    - 해시 함수는 빠른 연산 속도가 장점이지만, 고성능 GPU를 이용해 무차별적으로 해시 함수를 대입하면 같은 다이제스트를 찾을 수 있음
- **bcrypt**는 **Salting** 방식을 통합하여 위와 같은 취약점을 보완

### Salting

![](https://static.blex.me/images/content/2023/11/1/202311123_W1ZPnc6cf5cZQaqpwJSb.png)
- 임의의 `salt` 값을 덧붙여 **hash 함수**를 진행하는 것
- 같은 비밀번호여도 다른 다이제스트 값을 발생시켜, 다이제스트 값끼리 비교하기 어렵게 함

### Bcrypt 사용법

- bcrypt 설치

```shell
npm install bcrypt
# 타입스크립트(typescript) 사용할 경우
npm install -D @types/bcrypt
```

- 비밀번호 암호화

```typescript
import * as bcrypt from 'bcrypt';
// typescript 프로젝트에서 CommonJS 모듈을 사용할 경우 사용

/* password: 비밀번호
 * saltRound: 암호화 연산에 사용되는 salt의 cost, 높을수록 속도는 느려진다. */
const password = 'helloworld!';
const saltRound = 10;

// 비동기 콜백 방식
bcrypt.hash(password, saltRound, (err, salt) => {
  // 콜백 함수 내
});

// 비동기 방식
awiat
bcrypt.hash(password, saltRound);

// 동기 방식 
bcrypt.hashSync(password, saltRound);
```

- 같은 패스워드여도 bcrypt 해시 함수를 사용할 경우 결과값이 나온다.

```typescript
// 첫 번째 결과
$2b$10$jT8I0bOm7TbXpVCyy6E81.4i4xkovLK5JR1FMRhPu.PjQT5j0gtbC
// 두 번째 결과
$2b$10$qm7LX5KjZ0saJvDEjS3eHuBF34SZshumNBGwMHJYy0CGWmy23bCmq
// 세 번째 결과
$2b$10$2cdNyz9.rGEQekSCCYTF3eA.iYfj4numnz0P9qyaH5aiVITH8Lafy
```

- 비밀번호 검증

```typescript
import * as bcrypt from 'bcrypt';
// typescript 프로젝트에서 CommonJS 모듈을 사용할 경우 사용

/* password: 비밀번호  
 * hash: 암호화된 비밀번호 */
const password = 'helloworld!';

// 비동기 콜백 방식
bcrypt.compare(password, hash, (err, res) => {
  console.log(res); // 비교 결과 출력
});

// 비동기 방식
await bcrypt.compare(password, hash);

// 동기
bcrypt.compareSync(password, hash);
```

```typescript
const password = 'helloworld!';

const hash01 = '$2b$10$jT8I0bOm7TbXpVCyy6E81.4i4xkovLK5JR1FMRhPu.PjQT5j0gtbC';
const hash02 = '$2b$10$qm7LX5KjZ0saJvDEjS3eHuBF34SZshumNBGwMHJYy0CGWmy23bCmq';
const hash03 = '$2b$10$2cdNyz9.rGEQekSCCYTF3eA.iYfj4numnz0P9qyaH5aiVITH8Lafy';

console.log(bcrypt.compareSync(password, hash01)); // true
console.log(bcrypt.compareSync(password, hash02)); // true
console.log(bcrypt.compareSync(password, hash03)); // true
```

### 활용 코드

- `Entity`에서 데이터를 삽입할 경우, `BeforeInsert` 데코레이터를 통해 데이터베이스에 삽입하기 전 암호화를 진행하고 데이터베이스에 삽입한다.
```typescript
import {
  BeforeInsert,
  BeforeUpdate,
  Column,
  CreateDateColumn,
  Entity,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
} from 'typeorm';
import * as bcrypt from 'bcrypt';

@Entity('users')
export class User {
  @PrimaryGeneratedColumn()
  id!: number;

  @Column({
    type: 'varchar',
    length: 100,
    unique: true,
  })
  username!: string;

  @Column({
    type: 'varchar',
    length: 100,
  })
  email!: string;

  @Column({
    type: 'varchar',
    length: 200,
  })
  password!: string;

  @Column({ name: 'is_active', default: false })
  isActive!: boolean;

  @CreateDateColumn({ name: 'created_at' })
  createdAt!: Date;

  @UpdateDateColumn({ name: 'updated_at' })
  updatedAt!: Date;

  @BeforeInsert()
  @BeforeUpdate()
  private async beforeInsert() {
    this.password = await bcrypt.hash(this.password, 10);
  }
}
```

# 참고 자료
- [bcrypt 공식 문서, npm](https://www.npmjs.com/package/bcrypt)
- [bcrypt를 통해 비밀번호 암호화하기!, hxyxneee, velog](https://velog.io/@hxyxneee/bcrypt%EB%A5%BC-%ED%86%B5%ED%95%B4-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%95%94%ED%98%B8%ED%99%94%ED%95%98%EA%B8%B0)
- [bcrypt 모듈 암호화 원리 & 사용법, 인파, Tistory](https://inpa.tistory.com/entry/NODE-%F0%9F%93%9A-bcrypt-%EB%AA%A8%EB%93%88-%EC%9B%90%EB%A6%AC-%EC%82%AC%EC%9A%A9%EB%B2%95)
- [bcrypt로 비밀번호를 보호하자, choice91, Tistory](https://choice91.tistory.com/36)
