Security
SecurityIntermediate

Data Encryption Best Practices: A Guide to Securing Sensitive Information

DeveloperHat Team
9 min read
EncryptionData ProtectionSecurityCryptography

TL;DR

Learn how to implement robust data encryption strategies, including database encryption, file system encryption, and key management

import { MermaidDiagram } from '@/components/mermaid-diagram'

Learn how to implement robust data encryption strategies to protect sensitive information. This comprehensive guide covers encryption methods, key management, and best practices.

diagram={

graph TB

subgraph "Key Management"

KMS["Key Management Service"]

Master["Master Key"]

Data["Data Key"]

end

subgraph "Encryption"

Plain["Plaintext"]

Encrypt["Encryption"]

Cipher["Ciphertext"]

end

subgraph "Storage"

DB["Database"]

File["File System"]

Cloud["Cloud Storage"]

end

KMS --> Master

Master --> Data

Data --> Encrypt

Plain --> Encrypt

Encrypt --> Cipher

Cipher --> DB

Cipher --> File

Cipher --> Cloud

style KMS fill:#3b82f6,stroke:#2563eb,color:white

style Master fill:#3b82f6,stroke:#2563eb,color:white

style Data fill:#3b82f6,stroke:#2563eb,color:white

style Plain fill:#f1f5f9,stroke:#64748b

style Encrypt fill:#f1f5f9,stroke:#64748b

style Cipher fill:#f1f5f9,stroke:#64748b

style DB fill:#f1f5f9,stroke:#64748b

style File fill:#f1f5f9,stroke:#64748b

style Cloud fill:#f1f5f9,stroke:#64748b

}

/>

$1

Encryption methods include:

1. Symmetric: Same key for encryption/decryption

2. Asymmetric: Public/private key pairs

3. Hybrid: Combines both approaches

4. End-to-End: Data encrypted throughout transit

$1

$1

Implement secure database encryption:

``typescript

// database-encryption.ts

import { createCipheriv, createDecipheriv, randomBytes, scrypt } from 'crypto';

import { promisify } from 'util';

interface EncryptionConfig {

algorithm: string;

keyLength: number;

ivLength: number;

}

class DatabaseEncryption {

private readonly config: EncryptionConfig;

private readonly salt: Buffer;

private key: Buffer | null = null;

constructor(config: EncryptionConfig = {

algorithm: 'aes-256-gcm',

keyLength: 32,

ivLength: 16

}) {

this.config = config;

this.salt = randomBytes(32);

}

async initialize(password: string): Promise {

try {

const scryptAsync = promisify(scrypt);

this.key = await scryptAsync(password, this.salt, this.config.keyLength) as Buffer;

} catch (error) {

throw new Error(Error initializing encryption: ${error.message});

}

}

async encrypt(data: string): Promise<{

iv: string;

encryptedData: string;

authTag: string;

}> {

if (!this.key) {

throw new Error('Encryption not initialized');

}

try {

const iv = randomBytes(this.config.ivLength);

const cipher = createCipheriv(

this.config.algorithm,

this.key,

iv,

{ authTagLength: 16 }

);

let encryptedData = cipher.update(data, 'utf8', 'hex');

encryptedData += cipher.final('hex');

const authTag = cipher.getAuthTag();

return {

iv: iv.toString('hex'),

encryptedData,

authTag: authTag.toString('hex')

};

} catch (error) {

throw new Error(Encryption error: ${error.message});

}

}

async decrypt(encryptedData: string, iv: string, authTag: string): Promise {

if (!this.key) {

throw new Error('Encryption not initialized');

}

try {

const decipher = createDecipheriv(

this.config.algorithm,

this.key,

Buffer.from(iv, 'hex'),

{ authTagLength: 16 }

);

decipher.setAuthTag(Buffer.from(authTag, 'hex'));

let decryptedData = decipher.update(encryptedData, 'hex', 'utf8');

decryptedData += decipher.final('utf8');

return decryptedData;

} catch (error) {

throw new Error(Decryption error: ${error.message});

}

}

async encryptField>(

document: T,

fields: string[]

): Promise {

const encryptedDoc = { ...document };

for (const field of fields) {

if (field in document && typeof document[field] === 'string') {

const encrypted = await this.encrypt(document[field]);

encryptedDoc[field] = JSON.stringify(encrypted);

}

}

return encryptedDoc;

}

async decryptField>(

document: T,

fields: string[]

): Promise {

const decryptedDoc = { ...document };

for (const field of fields) {

if (field in document && typeof document[field] === 'string') {

const { iv, encryptedData, authTag } = JSON.parse(document[field]);

decryptedDoc[field] = await this.decrypt(encryptedData, iv, authTag);

}

}

return decryptedDoc;

}

}

// Example usage

async function main() {

const encryption = new DatabaseEncryption();

await encryption.initialize('secure-password');

// Encrypt document fields

const document = {

id: '123',

name: 'John Doe',

ssn: '123-45-6789',

email: 'john@example.com'

};

const sensitiveFields = ['ssn', 'email'];

const encryptedDoc = await encryption.encryptField(document, sensitiveFields);

console.log('Encrypted document:', encryptedDoc);

// Decrypt document fields

const decryptedDoc = await encryption.decryptField(encryptedDoc, sensitiveFields);

console.log('Decrypted document:', decryptedDoc);

}

main().catch(console.error);

`

$1

Implement secure file system encryption:

`typescript

// file-encryption.ts

import { createReadStream, createWriteStream } from 'fs';

import { pipeline } from 'stream/promises';

import { createCipheriv, createDecipheriv, randomBytes, scrypt } from 'crypto';

import { promisify } from 'util';

interface FileEncryptionConfig {

algorithm: string;

keyLength: number;

ivLength: number;

chunkSize: number;

}

class FileEncryption {

private readonly config: FileEncryptionConfig;

private readonly salt: Buffer;

private key: Buffer | null = null;

constructor(config: FileEncryptionConfig = {

algorithm: 'aes-256-gcm',

keyLength: 32,

ivLength: 16,

chunkSize: 64 * 1024 // 64KB chunks

}) {

this.config = config;

this.salt = randomBytes(32);

}

async initialize(password: string): Promise {

try {

const scryptAsync = promisify(scrypt);

this.key = await scryptAsync(password, this.salt, this.config.keyLength) as Buffer;

} catch (error) {

throw new Error(Error initializing encryption: ${error.message});

}

}

async encryptFile(

inputPath: string,

outputPath: string,

metadata: Record = {}

): Promise {

if (!this.key) {

throw new Error('Encryption not initialized');

}

try {

const iv = randomBytes(this.config.ivLength);

const cipher = createCipheriv(

this.config.algorithm,

this.key,

iv,

{ authTagLength: 16 }

);

// Write IV and metadata at the beginning of the file

const header = {

iv: iv.toString('hex'),

metadata

};

const output = createWriteStream(outputPath);

output.write(JSON.stringify(header) + '\n');

const input = createReadStream(inputPath, {

highWaterMark: this.config.chunkSize

});

await pipeline(input, cipher, output);

// Get and write auth tag at the end

const authTag = cipher.getAuthTag();

output.write(authTag);

} catch (error) {

throw new Error(File encryption error: ${error.message});

}

}

async decryptFile(

inputPath: string,

outputPath: string

): Promise> {

if (!this.key) {

throw new Error('Encryption not initialized');

}

try {

const input = createReadStream(inputPath, {

highWaterMark: this.config.chunkSize

});

// Read header

let headerStr = '';

for await (const chunk of input) {

headerStr += chunk;

if (headerStr.includes('\n')) {

break;

}

}

const header = JSON.parse(headerStr.split('\n')[0]);

const { iv, metadata } = header;

const decipher = createDecipheriv(

this.config.algorithm,

this.key,

Buffer.from(iv, 'hex'),

{ authTagLength: 16 }

);

const output = createWriteStream(outputPath);

await pipeline(input, decipher, output);

return metadata;

} catch (error) {

throw new Error(File decryption error: ${error.message});

}

}

async encryptDirectory(

inputDir: string,

outputDir: string,

options: {

include?: string[];

exclude?: string[];

metadata?: Record;

} = {}

): Promise {

// Implementation for directory encryption

}

async decryptDirectory(

inputDir: string,

outputDir: string,

options: {

include?: string[];

exclude?: string[];

} = {}

): Promise[]> {

// Implementation for directory decryption

return [];

}

}

// Example usage

async function main() {

const encryption = new FileEncryption();

await encryption.initialize('secure-password');

// Encrypt file

await encryption.encryptFile(

'sensitive.txt',

'encrypted.bin',

{ created: new Date().toISOString() }

);

// Decrypt file

const metadata = await encryption.decryptFile(

'encrypted.bin',

'decrypted.txt'

);

console.log('File metadata:', metadata);

}

main().catch(console.error);

`

$1

Implement secure transport:

`typescript

// secure-transport.ts

import { createServer, Server, TLSSocket } from 'tls';

import { readFileSync } from 'fs';

import { promisify } from 'util';

interface TLSConfig {

cert: string;

key: string;

ca?: string[];

requestCert?: boolean;

rejectUnauthorized?: boolean;

}

class SecureTransport {

private server: Server | null = null;

private readonly config: TLSConfig;

constructor(config: TLSConfig) {

this.config = {

...config,

requestCert: config.requestCert ?? true,

rejectUnauthorized: config.rejectUnauthorized ?? true

};

}

createSecureServer(

handler: (socket: TLSSocket) => void,

port: number = 8443

): Promise {

return new Promise((resolve, reject) => {

try {

const options = {

cert: readFileSync(this.config.cert),

key: readFileSync(this.config.key),

ca: this.config.ca?.map(ca => readFileSync(ca)),

requestCert: this.config.requestCert,

rejectUnauthorized: this.config.rejectUnauthorized

};

this.server = createServer(options, handler);

this.server.on('error', (error) => {

console.error('Server error:', error);

reject(error);

});

this.server.listen(port, () => {

console.log(Secure server listening on port ${port});

resolve();

});

} catch (error) {

reject(error);

}

});

}

async closeServer(): Promise {

if (this.server) {

const closeAsync = promisify(this.server.close.bind(this.server));

await closeAsync();

this.server = null;

}

}

}

// Example usage

async function main() {

const transport = new SecureTransport({

cert: '/path/to/cert.pem',

key: '/path/to/key.pem',

ca: ['/path/to/ca.pem']

});

await transport.createSecureServer((socket) => {

console.log('Client connected:', socket.authorized ? 'authorized' : 'unauthorized');

socket.on('data', (data) => {

console.log('Received:', data.toString());

socket.write('Echo: ' + data);

});

socket.on('error', (error) => {

console.error('Socket error:', error);

});

socket.on('end', () => {

console.log('Client disconnected');

});

});

}

main().catch(console.error);

`

$1

Implement secure key management:

`typescript

// key-management.ts

import { randomBytes, createHash, createCipheriv, createDecipheriv } from 'crypto';

import { promisify } from 'util';

import { readFile, writeFile } from 'fs/promises';

interface KeyConfig {

algorithm: string;

keyLength: number;

ivLength: number;

iterations: number;

}

class KeyManager {

private readonly config: KeyConfig;

private masterKey: Buffer | null = null;

private keys: Map = new Map();

constructor(config: KeyConfig = {

algorithm: 'aes-256-gcm',

keyLength: 32,

ivLength: 16,

iterations: 100000

}) {

this.config = config;

}

async initialize(password: string): Promise {

try {

const salt = randomBytes(32);

const scryptAsync = promisify(require('crypto').scrypt);

this.masterKey = await scryptAsync(

password,

salt,

this.config.keyLength,

{ N: this.config.iterations }

) as Buffer;

} catch (error) {

throw new Error(Error initializing key manager: ${error.message});

}

}

async generateKey(keyId: string): Promise {

if (!this.masterKey) {

throw new Error('Key manager not initialized');

}

try {

const key = randomBytes(this.config.keyLength);

const iv = randomBytes(this.config.ivLength);

const cipher = createCipheriv(

this.config.algorithm,

this.masterKey,

iv,

{ authTagLength: 16 }

);

let encryptedKey = cipher.update(key);

encryptedKey = Buffer.concat([encryptedKey, cipher.final()]);

const authTag = cipher.getAuthTag();

this.keys.set(keyId, key);

// Store encrypted key

await this.storeKey(keyId, {

iv: iv.toString('hex'),

key: encryptedKey.toString('hex'),

authTag: authTag.toString('hex')

});

return key;

} catch (error) {

throw new Error(Error generating key: ${error.message});

}

}

async getKey(keyId: string): Promise {

const cachedKey = this.keys.get(keyId);

if (cachedKey) {

return cachedKey;

}

if (!this.masterKey) {

throw new Error('Key manager not initialized');

}

try {

const storedKey = await this.loadKey(keyId);

const decipher = createDecipheriv(

this.config.algorithm,

this.masterKey,

Buffer.from(storedKey.iv, 'hex'),

{ authTagLength: 16 }

);

decipher.setAuthTag(Buffer.from(storedKey.authTag, 'hex'));

let key = decipher.update(Buffer.from(storedKey.key, 'hex'));

key = Buffer.concat([key, decipher.final()]);

this.keys.set(keyId, key);

return key;

} catch (error) {

throw new Error(Error retrieving key: ${error.message});

}

}

async rotateKey(keyId: string): Promise {

try {

const oldKey = await this.getKey(keyId);

const newKey = await this.generateKey(${keyId}_new);

// Re-encrypt data with new key

// Implementation depends on your specific use case

// Delete old key

this.keys.delete(keyId);

await this.deleteKey(keyId);

// Rename new key

this.keys.set(keyId, newKey);

this.keys.delete(${keyId}_new);

await this.renameKey(${keyId}_new, keyId);

} catch (error) {

throw new Error(Error rotating key: ${error.message});

}

}

private async storeKey(

keyId: string,

data: { iv: string; key: string; authTag: string }

): Promise {

await writeFile(

keys/${keyId}.json,

JSON.stringify(data),

{ encoding: 'utf8' }

);

}

private async loadKey(

keyId: string

): Promise<{ iv: string; key: string; authTag: string }> {

const data = await readFile(keys/${keyId}.json, { encoding: 'utf8' });

return JSON.parse(data);

}

private async deleteKey(keyId: string): Promise {

// Implementation for deleting key file

}

private async renameKey(oldId: string, newId: string): Promise {

// Implementation for renaming key file

}

}

// Example usage

async function main() {

const keyManager = new KeyManager();

await keyManager.initialize('master-password');

// Generate new key

const key1 = await keyManager.generateKey('key1');

console.log('Generated key:', key1.toString('hex'));

// Retrieve key

const retrievedKey = await keyManager.getKey('key1');

console.log('Retrieved key:', retrievedKey.toString('hex'));

// Rotate key

await keyManager.rotateKey('key1');

const rotatedKey = await keyManager.getKey('key1');

console.log('Rotated key:', rotatedKey.toString('hex'));

}

main().catch(console.error);

``

$1

1. Algorithm Selection

- Use strong algorithms

- Follow standards

- Regular updates

- Proper key lengths

2. Key Management

- Secure storage

- Regular rotation

- Access control

- Backup strategy

3. Implementation

- Input validation

- Error handling

- Logging

- Monitoring

4. Compliance

- Data regulations

- Industry standards

- Regular audits

- Documentation

$1

Effective encryption requires:

1. Strong algorithms

2. Secure key management

3. Proper implementation

4. Regular maintenance

5. Compliance monitoring

Remember to:

  • Follow best practices
  • Update regularly
  • Monitor usage
  • Document processes
  • Audit security
  • $1

    1. [NIST Encryption Guidelines](https://csrc.nist.gov/publications/detail/sp/800-175b/rev-1/final)

    2. [OWASP Cryptographic Storage](https://owasp.org/www-project-cheat-sheets/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html)

    3. [AWS KMS Best Practices](https://docs.aws.amazon.com/kms/latest/developerguide/best-practices.html)

    4. [Node.js Crypto Documentation](https://nodejs.org/api/crypto.html)

    5. [Encryption Standards](https://www.iso.org/standard/39727.html)

    Why This Matters

    Understanding the business and technical context helps you make informed decisions rather than blindly following patterns.

    Trade-offs to Consider

    Every architectural decision involves trade-offs. Consider your specific requirements, team expertise, and scale when evaluating options.

    When NOT to Use This

    Knowing when a solution doesn't apply is as valuable as knowing when it does. Consider alternatives for your specific situation.

    Decision Framework

    Use this framework to evaluate whether this approach is right for your use case based on your specific constraints and requirements.