Comprehensive Encryption and Security Service in NestJS: Argon2 Hashing, Token Generation, and AES Encryption
Published on Nov 11, 2024 | Reading time: 4 min
This technical guide presents a production-ready security service implementation for NestJS applications, integrating essential cryptographic operations while adhering to industry-standard security protocols. The EncryptionService
encapsulates critical functionality for modern backend systems, featuring Argon2 password hashing, AES-256 data encryption, and secure token generation within a modular architecture.
Core Service Configuration
The foundation establishes environment-aware cryptographic parameters using NestJS configuration patterns:
import {
BadRequestException,
Injectable,
InternalServerErrorException,
Logger,
} from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import * as argon2 from "argon2";
import { createCipheriv, createDecipheriv, createHash } from "crypto";
import * as uuid from "uuid";
import * as base64url from "base64url";
@Injectable()
export class EncryptionService {
private readonly logger = new Logger(EncryptionService.name);
private readonly algorithm = "aes-256-cbc";
private readonly ivLength = 16;
private readonly secretKey: string;
constructor(private configService: ConfigService) {
this.secretKey = this.configService.get<string>("ENCRYPTION_SECRET", "default_secret");
}
Security Architecture
- Algorithm Selection: AES-256-CBC provides FIPS-approved symmetric encryption
- IV Management: Fixed-length initialization vector for CBC mode compatibility
- Secret Handling: Environment variable injection prevents key hardcoding
- Fallback Mechanism: Default secret enables development environments while enforcing production configuration
Password Security Implementation
1. Argon2 Password Hashing
Implements memory-hard hashing for credential protection:
async hashPassword(rawPassword: string): Promise<string> {
if (!rawPassword) {
this.logger.error("Password is required");
throw new BadRequestException("Password is required");
}
try {
return await argon2.hash(rawPassword);
} catch (err) {
this.logger.error("Failed to hash password", err);
throw new InternalServerErrorException("Failed to hash password");
}
}
async verifyPassword(rawPassword: string = "", hashedPassword: string = ""): Promise<boolean> {
if (!rawPassword) {
this.logger.error("Password is required");
throw new BadRequestException("Password is required");
}
try {
return await argon2.verify(hashedPassword, rawPassword);
} catch (err) {
this.logger.error("Failed to verify password", err);
throw new InternalServerErrorException("Failed to verify password");
}
}
Enterprise Security Features
- Timing Attack Resistance: Constant-time verification implementation
- Memory Hardness: Configurable memory/iteration parameters (implicit in argon2.hash)
- Validation Enforcement: Strict input checking prevents empty credential processing
2. Temporary Password Generation
Implements complex password synthesis for recovery workflows:
generateTemporaryPassword(length = 10) {
const lowercaseChars = "abcdefghijklmnopqrstuvwxyz";
const uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const numericChars = "0123456789";
const specialChars = "!@#$%^&*()-_+=";
const allChars = lowercaseChars + uppercaseChars + numericChars + specialChars;
let password = "";
try {
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * allChars.length);
password += allChars[randomIndex];
}
} catch (error) {
throw new InternalServerErrorException("Failed to generate a password");
}
return password;
}
Security Considerations
- Character Diversity: Four distinct character classes minimize pattern predictability
- Length Configuration: Adjustable complexity through parameterization
- Entropy Management: Combined character pools enhance combinatorial complexity
Cryptographic Token Management
UUID-Based Token Generation
Creates collision-resistant identifiers for session management:
generateUniqueToken(length: number = 3): string {
const mergedUuid = Array.from({ length }, () => uuid.v4()).join("");
const tokenBuffer = Buffer.from(mergedUuid.replace(/-/g, ""), "hex");
return base64url.default(tokenBuffer);
}
Cryptographic Properties
- UUID v4 Utilization: 122-bit random number space guarantees uniqueness
- Base64URL Encoding: URL-safe representation for web compatibility
- Concatenation Strategy: Multiple UUIDs enhance token entropy
Symmetric Data Encryption
1. AES-256-CBC Encryption
Implements FIPS 197-compliant data protection:
encryptString(text: string): string {
if (!text) throw new Error("Text is required for encryption");
try {
const key = createHash("sha256").update(this.secretKey).digest("base64").slice(0, 32);
const iv = Buffer.alloc(this.ivLength, 0);
const cipher = createCipheriv(this.algorithm, key, iv);
let encrypted = cipher.update(text, "utf8", "base64");
encrypted += cipher.final("base64");
return encrypted.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
} catch (error) {
this.logger.error("Encryption failed", error);
throw new InternalServerErrorException("Encryption failed");
}
}
2. AES-256-CBC Decryption
Ensures reliable data recovery process:
decryptString(cipherText: string): string {
if (!cipherText) throw new Error("Cipher text is required for decryption");
try {
const base64Text = cipherText.replace(/-/g, "+").replace(/_/g, "/");
const key = createHash("sha256").update(this.secretKey).digest("base64").slice(0, 32);
const iv = Buffer.alloc(this.ivLength, 0);
const decipher = createDecipheriv(this.algorithm, key, iv);
let decrypted = decipher.update(base64Text, "base64", "utf8");
decrypted += decipher.final("utf8");
return decrypted;
} catch (error) {
this.logger.error("Decryption failed", error);
throw new InternalServerErrorException("Decryption failed");
}
}
Cryptographic Implementation Details
- Key Derivation: SHA-256 hashing ensures fixed-length key material
- IV Strategy: Zero-byte initialization vector (production implementations should use random IVs)
- Encoding Handling: Base64URL conversion for web-safe ciphertext representation
Service Modularization
Global module declaration enables cross-application accessibility:
import { Global, Module } from "@nestjs/common";
import { EncryptionService } from "./encryption.service";
@Global()
@Module({
providers: [EncryptionService],
exports: [EncryptionService],
})
export class EncryptionModule {}
Architectural Benefits
- Singleton Pattern: Single service instance across application modules
- Dependency Management: Centralized cryptographic functionality
- Security Consistency: Uniform implementation across application layers
Production Deployment Considerations
-
Secret Management
- Implement rotational policies for encryption secrets
- Integrate with vault services for secure key storage
-
Performance Optimization
- Benchmark Argon2 parameters against server capabilities
- Consider hardware acceleration for cryptographic operations
-
Compliance Requirements
- Align with PCI-DSS for payment data handling
- Implement FIPS 140-2 validation where required
-
Attack Mitigation
- Rate-limiting for password verification attempts
- Automated secret rotation mechanisms
This implementation provides a robust security foundation for NestJS applications, addressing critical requirements for:
- Credential Protection: Memory-hard hashing prevents brute-force attacks
- Data Confidentiality: Strong encryption safeguards sensitive information
- Session Security: Unpredictable tokens mitigate session hijacking risks
- Operational Reliability: Comprehensive error handling ensures system stability
The modular design facilitates seamless integration with enterprise security ecosystems, providing a critical layer in defense-in-depth strategies while maintaining developer productivity through standardized cryptographic primitives.