Implementing AES-256-CBC encryption securely in Node.js using the crypto module requires careful management of both the encryption key and the initialization vector (IV). Below is a step-by-step guide with an example that emphasizes secure key and IV management.
1. Key Management
For AES-256-CBC, you need a 256-bit key (32 bytes). It’s crucial not to hardcode this key directly in your source code. Instead, consider these best practices:
- Environment Variables: Store your encryption key in an environment variable. This prevents exposing it in your source code.
- Key Derivation: If you are generating the key from a passphrase, use a key derivation function (KDF) like PBKDF2.
2. IV Generation
The IV should be unique and random for each encryption operation. It doesn’t need to be secret, but it should not be reused with the same key. Here’s how to implement AES-256-CBC encryption securely in Node.js:
const crypto = require('crypto');
const { randomBytes } = require('crypto');
// Function to generate a random IV
function generateIV() {
return randomBytes(16); // AES block size is 16 bytes
}
// Encrypt function
function encrypt(text, key) {
const iv = generateIV(); // Generate a new IV for each encryption
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
// Return the IV and encrypted data, both in hex format
return {
iv: iv.toString('hex'),
encryptedData: encrypted,
};
}
// Decrypt function
function decrypt(encryptedData, key, iv) {
const decipher = crypto.createDecipheriv('aes-256-cbc', key, Buffer.from(iv, 'hex'));
let decrypted = decipher.update(encryptedData, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
// Example usage
const key = crypto.randomBytes(32); // Ideally, load this from a secure source
const sensitiveData = "This is a secret message.";
const encrypted = encrypt(sensitiveData, key);
console.log('Encrypted:', encrypted);
const decrypted = decrypt(encrypted.encryptedData, key, encrypted.iv);
console.log('Decrypted:', decrypted);
Key and IV Management Best Practices
-
Key Storage:
- Use a secure secrets management solution (like AWS Secrets Manager, HashiCorp Vault, or similar) to store your encryption key.
- Ensure that access to the key is restricted to only those services or users that absolutely need it.
-
IV Handling:
- Always generate a new IV for each encryption operation.
- Store the IV alongside the encrypted data (as shown in the example). When decrypting, you will need both the encrypted data and the IV.
-
Secure Key Rotation:
- Implement a strategy for periodically rotating your encryption keys. This adds an additional layer of security.
-
Logging and Monitoring:
- Monitor access to your keys and encrypted data. Implement logging to track any unauthorized access attempts.