From f2c70c44d2a2fbeefdd24add7a4cbe12b0187f97 Mon Sep 17 00:00:00 2001 From: tebrihk Date: Thu, 23 Apr 2026 07:48:12 +0100 Subject: [PATCH] Password Hashing Configuration --- .env.example | 3 ++ README.md | 58 ++++++++++++++++++++++++++++++++++-- src/config/env.validation.ts | 3 ++ src/users/users.service.ts | 10 +++++-- 4 files changed, 69 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index 858636ca..3c899d9d 100644 --- a/.env.example +++ b/.env.example @@ -16,6 +16,9 @@ JWT_EXPIRES_IN=15m JWT_REFRESH_SECRET=your-super-secret-refresh-key-change-this-in-production JWT_REFRESH_EXPIRES_IN=7d +# Security Configuration +BCRYPT_ROUNDS=10 + # Redis Configuration (for Bull Queue) REDIS_HOST=localhost REDIS_PORT=6379 diff --git a/README.md b/README.md index f05395db..91a7d742 100644 --- a/README.md +++ b/README.md @@ -136,9 +136,63 @@ TeachLink Backend provides secure and scalable APIs to power features such as: | Auth | JWT + Wallet Sign-In | | Deployment | Docker, Railway, or Fly.io | | File Upload | Cloudinary | -| Documentation | Swagger | +| Security | Helmet + bcrypt | Security headers and password hashing | -## �️ Database +## 🔐 Security + +### Password Hashing Configuration + +The application uses **bcrypt** for password hashing with configurable rounds via the `BCRYPT_ROUNDS` environment variable. + +#### Recommended Bcrypt Rounds by Environment + +| Environment | Recommended Rounds | Hash Time (ms) | Security Level | Performance Impact | +| ----------- | ----------------- | -------------- | -------------- | ------------------ | +| **Development** | 8-10 | 50-100 | Good | Low | +| **Staging** | 10-12 | 100-300 | High | Medium | +| **Production** | 12-14 | 300-1000 | Very High | High | + +#### Security vs Performance Tradeoffs + +**Lower Rounds (4-8):** +- ✅ Faster authentication +- ✅ Lower CPU usage +- ⚠️ Reduced security against brute force attacks +- ⚠️ May be vulnerable to GPU-based cracking + +**Higher Rounds (12-15):** +- ✅ Strong resistance against brute force attacks +- ✅ Future-proof against computational advances +- ❌ Slower authentication (may impact user experience) +- ❌ Higher CPU usage (may affect scalability) + +#### Configuration Example + +```env +# Development (faster, less secure) +BCRYPT_ROUNDS=8 + +# Production (slower, more secure) +BCRYPT_ROUNDS=12 +``` + +#### Security Best Practices + +1. **Minimum 10 rounds** for production environments +2. **Monitor authentication performance** when increasing rounds +3. **Consider rate limiting** to prevent brute force attacks +4. **Use hardware security modules** for high-security applications +5. **Regular security audits** to assess adequate protection levels + +#### Migration Considerations + +When changing `BCRYPT_ROUNDS`: +- Existing passwords remain valid until users change them +- New passwords will use the configured rounds +- Consider forcing password reset for sensitive accounts +- Gradually increase rounds to monitor performance impact + +## 🗄️ Database ### Index Strategy diff --git a/src/config/env.validation.ts b/src/config/env.validation.ts index 5a651d45..5e98dcef 100644 --- a/src/config/env.validation.ts +++ b/src/config/env.validation.ts @@ -29,6 +29,9 @@ export const envValidationSchema = Joi.object({ // Encryption ENCRYPTION_SECRET: Joi.string().min(32).required(), + // Security Configuration + BCRYPT_ROUNDS: Joi.number().integer().min(4).max(15).default(10), + // Stripe Configuration STRIPE_SECRET_KEY: Joi.string().required(), STRIPE_WEBHOOK_SECRET: Joi.string().required(), diff --git a/src/users/users.service.ts b/src/users/users.service.ts index b3655186..fb09af3b 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -13,6 +13,7 @@ import { GetUsersDto } from './dto/get-users.dto'; import { CachingService } from '../caching/caching.service'; import { CACHE_TTL, CACHE_PREFIXES, CACHE_EVENTS } from '../caching/caching.constants'; import { EventEmitter2 } from '@nestjs/event-emitter'; +import { ConfigService } from '@nestjs/config'; @Injectable() export class UsersService { @@ -21,7 +22,8 @@ export class UsersService { private readonly userRepository: Repository, private readonly cachingService: CachingService, private readonly eventEmitter: EventEmitter2, - ) {} + private readonly configService: ConfigService, + ) { } async create(createUserDto: CreateUserDto): Promise { // Check if user already exists @@ -31,7 +33,8 @@ export class UsersService { ensureUserDoesNotExist(existingUser, 'User with this email already exists'); // Hash password - const hashedPassword = await bcrypt.hash(createUserDto.password, 10); + const bcryptRounds = this.configService.get('BCRYPT_ROUNDS') || 10; + const hashedPassword = await bcrypt.hash(createUserDto.password, bcryptRounds); // Create user const user = this.userRepository.create({ @@ -140,7 +143,8 @@ export class UsersService { // Append current, maintain last 5 entries user.passwordHistory = [...recentPasswords, user.password].slice(-5); - updateUserDto.password = await bcrypt.hash(plainPassword, 10); + const bcryptRounds = this.configService.get('BCRYPT_ROUNDS') || 10; + updateUserDto.password = await bcrypt.hash(plainPassword, bcryptRounds); } Object.assign(user, updateUserDto);