auth/Documentaion/security/DATABASE_ENCRYPTION_SETUP.md

370 lines
9.9 KiB
Markdown

# 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=<your-32-byte-base64-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 <password> \
--allocated-storage 20 \
--storage-encrypted \
--kms-key-id <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=<kms-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