Encryption
Overview
The starter kit provides built-in encryption capabilities to secure sensitive data both at rest and in transit. This documentation covers the encryption techniques and utilities implemented in the starter kit.
Configuration
Configure encryption settings in your .env
file:
# Encryption settings
ENCRYPTION_KEY=your_32_character_encryption_key
ENCRYPTION_IV=your_16_character_initialization_vector
Encryption Service
The EncryptionService
provides methods for encrypting and decrypting data using AES-256-CBC encryption.
@Injectable()
export class EncryptionService {
constructor(private configService: ConfigService) {}
private getEncryptionKey(): Buffer {
const key = this.configService.get<string>('ENCRYPTION_KEY');
if (!key || key.length !== 32) {
throw new Error('Invalid encryption key');
}
return Buffer.from(key);
}
private getEncryptionIv(): Buffer {
const iv = this.configService.get<string>('ENCRYPTION_IV');
if (!iv || iv.length !== 16) {
throw new Error('Invalid encryption IV');
}
return Buffer.from(iv);
}
encrypt(text: string): string {
const cipher = createCipheriv(
'aes-256-cbc',
this.getEncryptionKey(),
this.getEncryptionIv(),
);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
decrypt(encryptedText: string): string {
const decipher = createDecipheriv(
'aes-256-cbc',
this.getEncryptionKey(),
this.getEncryptionIv(),
);
let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
}
TypeORM Encryption Transformer
The starter kit includes a custom TypeORM transformer to automatically encrypt/decrypt entity fields:
export class EncryptionTransformer {
constructor(private encryptionService: EncryptionService) {}
to(value: string): string {
if (value === null || value === undefined) {
return value;
}
return this.encryptionService.encrypt(value);
}
from(value: string): string {
if (value === null || value === undefined) {
return value;
}
return this.encryptionService.decrypt(value);
}
}
Usage
Encrypting Entity Fields
@Entity('users')
export class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
email: string;
// Automatically encrypted in the database
@Column({
transformer: new EncryptionTransformer(Container.get(EncryptionService)),
})
ssn: string;
// Other fields...
}
Manual Encryption
@Injectable()
export class UserService {
constructor(private encryptionService: EncryptionService) {}
async storeSecureData(userId: string, data: string): Promise<void> {
const encryptedData = this.encryptionService.encrypt(data);
// Store encrypted data...
}
async getSecureData(userId: string): Promise<string> {
// Retrieve encrypted data...
const encryptedData = '...';
return this.encryptionService.decrypt(encryptedData);
}
}
Security Best Practices
Key Management:
- Never hardcode encryption keys in your source code
- Use environment variables or a secure key management service
- Rotate keys periodically
Securing Environment Variables:
- Use
.env
files only for development - For production, use secure environment variable management
- Consider using a secrets manager like AWS Secrets Manager, HashiCorp Vault, etc.
- Use
Transport Layer Security:
- Always deploy your application with HTTPS
- Use appropriate SSL/TLS configurations
- Consider using HSTS (HTTP Strict Transport Security)
Data Access Controls:
- Implement proper authorization before accessing encrypted data
- Log all encryption/decryption operations for audit purposes
- Implement monitoring for suspicious decryption activities
Key Rotation
The starter kit includes utilities to help with encryption key rotation:
@Injectable()
export class KeyRotationService {
constructor(
private oldEncryptionService: EncryptionService,
private newEncryptionService: EncryptionService,
private dataRepository: Repository<EncryptedData>,
) {}
async rotateKeys(): Promise<void> {
const encryptedData = await this.dataRepository.find();
for (const data of encryptedData) {
const decrypted = this.oldEncryptionService.decrypt(data.value);
const reEncrypted = this.newEncryptionService.encrypt(decrypted);
data.value = reEncrypted;
await this.dataRepository.save(data);
}
}
}
Testing Encryption
The starter kit includes testing utilities for verifying encryption:
describe('EncryptionService', () => {
let service: EncryptionService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
EncryptionService,
{
provide: ConfigService,
useValue: {
get: jest.fn((key) => {
if (key === 'ENCRYPTION_KEY') return 'a'.repeat(32);
if (key === 'ENCRYPTION_IV') return 'b'.repeat(16);
return null;
}),
},
},
],
}).compile();
service = module.get<EncryptionService>(EncryptionService);
});
it('should encrypt and decrypt data correctly', () => {
const data = 'sensitive information';
const encrypted = service.encrypt(data);
expect(encrypted).not.toEqual(data);
expect(service.decrypt(encrypted)).toEqual(data);
});
});