-
nestJS password securityProject using Nest.js/E-commerce App 2021. 9. 3. 16:17
개요
Server는 Client 측에서 보내는 모든 data를 신뢰해서는 안 된다. Client에서도 filtering을 하겠지만 hacker가 마음을 먹으면 충분히 Client filter를 우회해서 값을 주입할 수 있다. 그렇기 때문에 server에서도 password 보안 정책을 해줘야만 한다.
user.entity.ts
import { Field, InputType, ObjectType, } from '@nestjs/graphql'; import { IsEmail, IsString, MinLength, } from 'class-validator'; import { BeforeInsert, BeforeUpdate, Column, Entity } from 'typeorm'; @InputType('UserInputType', { isAbstract: true }) @ObjectType() @Entity() export class User extends CoreEntity { @Column({ unique: true }) @Field((type) => String) @IsEmail() email: string; @Column() @Field((type) => String) @MinLength(8) password: string; @BeforeInsert() @BeforeUpdate() async hashPassword(): Promise<void> { if (this.password) { try { this.password = await bcrypt.hash(this.password, 10); } catch (e) { console.error(e); throw new InternalServerErrorException(); } } } async checkPassowrd(password: string): Promise<boolean> { try { const ok = await bcrypt.compare(password, this.password); return ok; } catch (error) { console.error(error); throw new InternalServerErrorException(); } } }
class-validator module을 이용해서 유효성 검사를 해준다. @MinLength(8)을 이용해서 비밀번호는 최소 8자리 이상이어야 한다.
@BeforeInsert와 @BeforeUpdate를 이용해서 계정을 생성하거나 비밀번호를 변경할 경우 plain text로 저장하지 않고 hash 값을 저장한다.
create-account.dto.ts
import { Field, InputType, ObjectType, PickType } from '@nestjs/graphql'; import { MinLength } from 'class-validator'; import { CoreOutput } from 'src/common/dtos/output.dto'; import { User } from '../entities/user.entity'; @InputType() export class CreateAccountInput extends PickType(User, [ 'email', 'password', ]) { @Field((type) => String) @MinLength(8) verifyPassword: string; } @ObjectType() export class CreateAccountOutput extends CoreOutput {}
DTO에도 @MinLength(8)를 이용해서 verifyPassword도 8자리 이상 받을 수 있도록 한다.
users.service.ts
async createAccount({ email, password, verifyPassword, }: CreateAccountInput): Promise<CreateAccountOutput> { try { const exists = await this.users.findOne({ email }); if (exists) { return { ok: false, error: 'There is a user with that email already' }; } if (password !== verifyPassword) { return { ok: false, error: 'Password does not match', }; } const regex = new RegExp( /(?=.*[!@#$%^&\*\(\)_\+\-=\[\]\{\};\':\"\\\|,\.<>\/\?]+)(?=.*[a-zA-Z]+)(?=.*\d+)/, ); const passwordTestPass = regex.test(password); if (!passwordTestPass) { return { ok: false, error: 'Password must contain special character, string and number', }; } const user = await this.users.save( this.users.create({ email, password }), ); return { ok: true }; } catch (e) { return { ok: false, error: "Couldn't create account" }; } }
사용자가 설정할 비밀번호와 확인 비밀번호(verifyPassword)가 일치하는지 확인한다. 그리고 비밀번호에 문자, 숫자, 특수문자가 들어있는지 확인하는 Regex를 사용한다. 이를 모두 통과할 경우 계정을 생성한다. change-password 같은 경우에서도 똑같고 추가로 current password와 new password가 다른지 확인해야 한다.
Github link
Set password security for create-account : https://github.com/zpskek/houpang-backend-v1/commit/b9010278adf64572df81e7a42998f3b1259cfc13
Set security password for change-password service : https://github.com/zpskek/houpang-backend-v1/commit/30674b50b8499cb1b017095d3b97dbe84826189f
'Project using Nest.js > E-commerce App' 카테고리의 다른 글
TypeORM의 @ManyToOne과 @OneToMany (0) 2021.09.03 nestJS에서 DTO에서 쓰는 PartialType and PickType을 같이 쓸 때 주의할 점 (0) 2021.09.03 nest.js authGuard and authUser decorator (0) 2021.09.02 JWT with nestJS (0) 2021.09.02 login API on nestJS using graphql (0) 2021.09.02