# Database Encryption Setup Guide **Security Hardening: Database Compromise Mitigation** This guide covers the implementation of database encryption at multiple levels to protect sensitive data (PII) like phone numbers. --- ## ✅ **IMPLEMENTED: Field-Level Encryption** ### **What's Implemented** 1. **Field-Level Encryption for Phone Numbers** - Phone numbers are encrypted using AES-256-GCM before storing in database - Automatic decryption when reading from database - Backward compatibility with existing plaintext data 2. **Database Access Logging** - All database queries are logged (configurable) - Logs include: query type, tables accessed, user context, IP address, timestamp - Sensitive parameters are redacted in logs ### **Configuration** #### **1. Enable Field-Level Encryption** Add to your `.env` file: ```bash # Enable field-level encryption ENCRYPTION_ENABLED=true # Generate encryption key (32 bytes, base64 encoded) # Run: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" ENCRYPTION_KEY= ``` **Generate Encryption Key:** ```bash node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" ``` **Example output:** ``` K8mN3pQ9rT5vW7xY1zA2bC4dE6fG8hI0jK2lM4nO6pQ8rS0tU2vW4xY6zA= ``` ⚠️ **IMPORTANT:** - Store encryption key in secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.) - Never commit encryption keys to version control - Rotate keys periodically (requires data re-encryption) #### **2. Enable Database Access Logging** Add to your `.env` file: ```bash # Enable database access logging DB_ACCESS_LOGGING_ENABLED=true # Log level: 'all' (all queries) or 'sensitive' (only sensitive tables) DB_ACCESS_LOG_LEVEL=sensitive ``` **Sensitive Tables (always logged if enabled):** - `users` - `otp_codes` - `otp_requests` - `refresh_tokens` - `auth_audit` - `user_devices` ### **How It Works** #### **Encryption Flow:** 1. Application receives plaintext phone number 2. Phone number is encrypted using AES-256-GCM 3. Encrypted value is stored in database 4. When reading, encrypted value is automatically decrypted #### **Backward Compatibility:** - Existing plaintext phone numbers continue to work - System searches for both encrypted and plaintext values - New records are stored encrypted - Old records can be migrated gradually #### **Database Access Logging:** - All queries to sensitive tables are logged - Logs include sanitized parameters (no sensitive data) - User context (user ID, IP, user agent) is captured - Query duration is tracked ### **Code Usage** #### **Encrypt Phone Number:** ```javascript const { encryptPhoneNumber } = require('./utils/fieldEncryption'); const encryptedPhone = encryptPhoneNumber('+919876543210'); // Store encryptedPhone in database ``` #### **Decrypt Phone Number:** ```javascript const { decryptPhoneNumber } = require('./utils/fieldEncryption'); const plaintextPhone = decryptPhoneNumber(encryptedPhoneFromDb); // Use plaintextPhone in application ``` #### **Database Query with Context:** ```javascript const db = require('./db'); // Create context from request const context = db.createContextFromRequest(req); // Query with context (for logging) const result = await db.query( 'SELECT * FROM users WHERE id = $1', [userId], context ); ``` --- ## ⚠️ **REQUIRED: Database-Level Encryption (TDE)** ### **What's Needed** **Transparent Data Encryption (TDE)** must be configured at the database server level. This is infrastructure-level encryption that protects data at rest. ### **PostgreSQL TDE Setup** PostgreSQL doesn't have built-in TDE, but you can use: #### **Option 1: PostgreSQL with pgcrypto Extension (Application-Level)** Already implemented via field-level encryption (see above). #### **Option 2: Database-Level Encryption (Infrastructure)** **For PostgreSQL on Cloud Providers:** 1. **AWS RDS PostgreSQL:** - Enable encryption at rest when creating RDS instance - Uses AWS KMS for key management - Encryption is transparent to application 2. **Google Cloud SQL:** - Enable encryption at rest in instance settings - Uses Google Cloud KMS 3. **Azure Database for PostgreSQL:** - Enable Transparent Data Encryption (TDE) - Uses Azure Key Vault 4. **Self-Hosted PostgreSQL:** - Use encrypted filesystem (LUKS, BitLocker) - Use PostgreSQL with encryption at filesystem level ### **Setup Instructions** #### **AWS RDS PostgreSQL:** 1. **Create Encrypted RDS Instance:** ```bash aws rds create-db-instance \ --db-instance-identifier farm-auth-db \ --db-instance-class db.t3.micro \ --engine postgres \ --master-username postgres \ --master-user-password \ --allocated-storage 20 \ --storage-encrypted \ --kms-key-id ``` 2. **Or Enable Encryption on Existing Instance:** - Create snapshot - Restore snapshot with encryption enabled - Update application connection string #### **Google Cloud SQL:** 1. **Enable Encryption:** ```bash gcloud sql instances create farm-auth-db \ --database-version=POSTGRES_14 \ --tier=db-f1-micro \ --storage-type=SSD \ --disk-size=20GB \ --disk-encryption-key= ``` #### **Azure Database for PostgreSQL:** 1. **Enable TDE:** - Navigate to Azure Portal - Select your PostgreSQL server - Go to "Data encryption" - Enable "Data encryption" - Select Key Vault key --- ## 📋 **Migration Plan** ### **Phase 1: Enable Field-Level Encryption (Current)** ✅ **Status: Implemented** 1. Set `ENCRYPTION_ENABLED=true` 2. Set `ENCRYPTION_KEY` (from secrets manager) 3. New records are automatically encrypted 4. Existing plaintext records continue to work (backward compatibility) ### **Phase 2: Migrate Existing Data** **Script to migrate existing plaintext phone numbers:** ```sql -- WARNING: This requires application-level decryption/encryption -- Run migration script that: -- 1. Reads plaintext phone numbers -- 2. Encrypts them using application encryption -- 3. Updates database with encrypted values -- Example (run via Node.js script, not direct SQL): -- const { encryptPhoneNumber } = require('./utils/fieldEncryption'); -- const users = await db.query('SELECT id, phone_number FROM users WHERE phone_number NOT LIKE \'%:%\''); -- for (const user of users.rows) { -- const encrypted = encryptPhoneNumber(user.phone_number); -- await db.query('UPDATE users SET phone_number = $1 WHERE id = $2', [encrypted, user.id]); -- } ``` ### **Phase 3: Enable Database-Level Encryption (TDE)** 1. **For Cloud Providers:** - Enable encryption at rest in database settings - No application changes needed (transparent) 2. **For Self-Hosted:** - Set up encrypted filesystem - Migrate database to encrypted volume - Update backup procedures --- ## 🔒 **Security Best Practices** ### **1. Key Management** - ✅ Store encryption keys in secrets manager (AWS Secrets Manager, HashiCorp Vault) - ✅ Rotate keys periodically (every 90 days recommended) - ✅ Use separate keys for different environments (dev, staging, prod) - ✅ Never commit keys to version control ### **2. Access Control** - ✅ Use least privilege for database users - ✅ Enable database access logging (`DB_ACCESS_LOGGING_ENABLED=true`) - ✅ Review access logs regularly - ✅ Set up alerts for suspicious access patterns ### **3. Backup Encryption** - ✅ Encrypt database backups - ✅ Store backup encryption keys separately - ✅ Test backup restoration procedures ### **4. Monitoring** - ✅ Monitor database access logs - ✅ Set up alerts for: - Unusual access patterns - Failed authentication attempts - Large data exports - Access from unexpected IPs --- ## 📊 **Compliance** ### **GDPR Compliance** - ✅ Personal data (phone numbers) is encrypted at rest - ✅ Access to personal data is logged - ✅ Encryption keys are managed securely ### **PCI DSS Compliance** - ✅ Sensitive data is encrypted - ✅ Access controls are in place - ✅ Audit logging is enabled --- ## 🚨 **Troubleshooting** ### **Issue: Encryption Not Working** **Symptoms:** - Phone numbers stored as plaintext - Decryption errors **Solutions:** 1. Check `ENCRYPTION_ENABLED=true` in environment 2. Verify `ENCRYPTION_KEY` is set and valid (32 bytes, base64) 3. Check application logs for encryption errors ### **Issue: Backward Compatibility Broken** **Symptoms:** - Existing users can't log in - Phone number lookups fail **Solutions:** 1. Ensure queries search for both encrypted and plaintext 2. Check that `decryptPhoneNumber` handles plaintext gracefully 3. Verify migration script completed successfully ### **Issue: Database Access Logging Not Working** **Symptoms:** - No entries in `db_access_log` table **Solutions:** 1. Check `DB_ACCESS_LOGGING_ENABLED=true` 2. Verify `db_access_log` table exists (auto-created on first log) 3. Check application logs for logging errors --- ## 📝 **Checklist** ### **Before Production:** - [ ] Generate encryption key and store in secrets manager - [ ] Set `ENCRYPTION_ENABLED=true` in production environment - [ ] Set `DB_ACCESS_LOGGING_ENABLED=true` in production - [ ] Enable database-level encryption (TDE) at infrastructure level - [ ] Test encryption/decryption with sample data - [ ] Verify backward compatibility with existing data - [ ] Set up monitoring for database access logs - [ ] Document key rotation procedure - [ ] Test backup and restore procedures - [ ] Review and update access control policies --- ## 🔗 **Related Documentation** - `SECURITY_AUDIT_REPORT.md` - Security audit findings - `src/utils/fieldEncryption.js` - Encryption implementation - `src/middleware/dbAccessLogger.js` - Database access logging - `src/db.js` - Database wrapper with logging support --- ## 📞 **Support** For questions or issues: 1. Check application logs for encryption/decryption errors 2. Review database access logs for suspicious activity 3. Verify environment variables are set correctly 4. Test with sample data before production deployment