단방향 암호화
- 한쪽 방향으로 암호화하는 것을 의미
암호화
는 가능하지만 복호화
는 불가능 함
- 양방향 암호화 : 암호화된 데이터를 복호화 할 수 있으며, 대칭키, 공개키 암호화 알고리즘 등이 있다.
해시(Hash) 함수
와 MD5
, SHA
등의 알고리즘이 있다.
Bcrypt
Blowfish 암호
에 기반을 둔 암호화 해시 함수
Niels Provos
와 David Mazières
가 설계
단방향 해시 함수의 한계점
을 극복하기 위해 설계
해시 함수의 한계점
- 레인보우 테이블 : 동일한 메시지는 동일한 다이제스트(digest)를 갖는 테이블로, 일반적인 해시 함수의 특징
- 무차별 대입 공격(Brute-Force Attack)에 취약
- 해시 함수는 빠른 연산 속도가 장점이지만, 고성능 GPU를 이용해 무차별적으로 해시 함수를 대입하면 같은 다이제스트를 찾을 수 있음
- bcrypt는 Salting 방식을 통합하여 위와 같은 취약점을 보완
Salting
- 임의의
salt
값을 덧붙여 hash 함수를 진행하는 것
- 같은 비밀번호여도 다른 다이제스트 값을 발생시켜, 다이제스트 값끼리 비교하기 어렵게 함
Bcrypt 사용법
npm install bcrypt
# 타입스크립트(typescript) 사용할 경우
npm install -D @types/bcrypt
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 해시 함수를 사용할 경우 결과값이 나온다.
// 첫 번째 결과
$2b$10$jT8I0bOm7TbXpVCyy6E81.4i4xkovLK5JR1FMRhPu.PjQT5j0gtbC
// 두 번째 결과
$2b$10$qm7LX5KjZ0saJvDEjS3eHuBF34SZshumNBGwMHJYy0CGWmy23bCmq
// 세 번째 결과
$2b$10$2cdNyz9.rGEQekSCCYTF3eA.iYfj4numnz0P9qyaH5aiVITH8Lafy
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);
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
데코레이터를 통해 데이터베이스에 삽입하기 전 암호화를 진행하고 데이터베이스에 삽입한다.
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);
}
}
참고 자료
Ghost