From b090db9143404435172bc5c270280911b253e8ac Mon Sep 17 00:00:00 2001 From: Juliano Sill Date: Wed, 20 May 2026 11:45:17 -0300 Subject: [PATCH] feat(cryptography): add pepper to password hashing --- .env.docker | 1 + .env.example | 1 + .env.test | 1 + src/app/cryptography/crypography.service.ts | 21 +++++++++++++++------ src/env/env.ts | 3 ++- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/.env.docker b/.env.docker index ae6ff33..b9b6c45 100644 --- a/.env.docker +++ b/.env.docker @@ -15,6 +15,7 @@ APP_URL=http://localhost:3000 COOKIE_DOMAIN=localhost COOKIE_SECRET=super-secret-here JWT_SECRET=super-secret-here +HASH_PEPPER=super-pepper-here # E-mails EMAIL_PROVIDER=none diff --git a/.env.example b/.env.example index 418e0fe..8320136 100644 --- a/.env.example +++ b/.env.example @@ -15,6 +15,7 @@ APP_URL="http://localhost:3000" COOKIE_DOMAIN="localhost" COOKIE_SECRET="super-secret-here" JWT_SECRET="super-secret-here" +HASH_PEPPER="super-pepper-here" # E-mails EMAIL_PROVIDER="none" diff --git a/.env.test b/.env.test index 2454e78..ed07f5a 100644 --- a/.env.test +++ b/.env.test @@ -15,6 +15,7 @@ APP_URL="http://localhost:3000" COOKIE_DOMAIN="localhost" COOKIE_SECRET="test-cookie-secret-key-for-testing-only" JWT_SECRET="test-jwt-secret-key-for-testing-only" +HASH_PEPPER="super-pepper-here" # E-mails EMAIL_PROVIDER="none" diff --git a/src/app/cryptography/crypography.service.ts b/src/app/cryptography/crypography.service.ts index 7b21c83..ab42f88 100644 --- a/src/app/cryptography/crypography.service.ts +++ b/src/app/cryptography/crypography.service.ts @@ -1,3 +1,5 @@ +import { createHmac } from 'node:crypto'; + import { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { compare, hash } from 'bcryptjs'; @@ -12,19 +14,26 @@ type SetCookieOptions = CookieOptions & { @Injectable() export class CryptographyService { - private readonly HASH_SALT_LENGTH = 10; - constructor( private readonly jwtService: JwtService, private readonly envService: EnvService, ) {} - createHash(plain: string): Promise { - return hash(plain, this.HASH_SALT_LENGTH); + async createHash(plain: string): Promise { + const rounds = this.envService.get('NODE_ENV') === 'test' ? 1 : 14; + const plainWithPepper = this.applyPepper(plain); + return await hash(plainWithPepper, rounds); + } + + async compareHash(plain: string, hash: string): Promise { + const plainWithPepper = this.applyPepper(plain); + return await compare(plainWithPepper, hash); } - compareHash(plain: string, hash: string): Promise { - return compare(plain, hash); + private applyPepper(value: string) { + const pepper = this.envService.get('HASH_PEPPER'); + if (!pepper) return value; + return createHmac('sha256', pepper).update(value).digest('base64'); } async verifyToken(token: string): Promise { diff --git a/src/env/env.ts b/src/env/env.ts index 212cbf3..a5a37d8 100644 --- a/src/env/env.ts +++ b/src/env/env.ts @@ -4,10 +4,10 @@ export const envSchema = z.object({ // Environment NODE_ENV: z.enum(['production', 'development', 'homolog', 'test']), APP_ENVIRONMENT: z.enum(['lambda', 'docker', 'local']), - MAINTENANCE: z.enum(['true', 'false']).transform((val) => val === 'true'), ENABLE_NEST_LOGS: z .enum(['true', 'false']) .transform((val) => val === 'true'), + MAINTENANCE: z.enum(['true', 'false']).transform((val) => val === 'true'), // API API_BASE_URL: z.string().url().optional(), @@ -20,6 +20,7 @@ export const envSchema = z.object({ COOKIE_DOMAIN: z.string().min(1), COOKIE_SECRET: z.string().min(1), JWT_SECRET: z.string().min(1), + HASH_PEPPER: z.string().min(1), // Database DB_HOST: z.string().min(1),