Compliance11 min read

Data Privacy by Design: GDPR, CCPA, and Modern Privacy Engineering

Implementing privacy-first architectures that comply with global data protection regulations through encryption, anonymization, consent management, and privacy-enhancing technologies.

DP

Privacy Engineering Team

Compliance & Security

Introduction

Data privacy is no longer optional—it's a fundamental requirement enforced by regulations like GDPR, CCPA, and emerging global privacy laws. Privacy by Design means building privacy protections into your systems from the ground up, not retrofitting them later. This guide covers practical privacy engineering techniques that satisfy regulatory requirements while protecting user data.

1. Data Classification and Inventory

Understanding what data you collect is the foundation of privacy compliance:

// Data classification schema
enum DataClassification {
  PUBLIC          // No restrictions
  INTERNAL        // Internal use only
  CONFIDENTIAL    // Sensitive business data
  PERSONAL        // PII under GDPR/CCPA
  SENSITIVE       // Special category data (health, biometric, etc.)
}

// TypeScript example with metadata
interface UserData {
  id: string;
  email: string;           // @privacy(classification: PERSONAL, retention: 730d)
  hashedPassword: string;  // @privacy(classification: SENSITIVE, retention: 730d)
  preferences: object;     // @privacy(classification: PERSONAL, retention: 365d)
  analyticsId: string;     // @privacy(classification: INTERNAL, pseudonymized: true)
  ipAddress: string;       // @privacy(classification: PERSONAL, anonymize: 90d)
}

// Automated data mapping
const dataInventory = {
  databases: {
    users: {
      tables: {
        users: {
          columns: {
            email: { classification: 'PERSONAL', purpose: 'Authentication' },
            name: { classification: 'PERSONAL', purpose: 'Account management' },
            birthdate: { classification: 'SENSITIVE', purpose: 'Age verification' }
          }
        }
      }
    }
  }
};

2. Consent Management

Granular Consent Framework

// Consent management system
interface ConsentRecord {
  userId: string;
  timestamp: Date;
  purposes: {
    necessary: boolean;      // Always true (legitimate interest)
    analytics: boolean;      // Opt-in
    marketing: boolean;      // Opt-in
    thirdParty: boolean;     // Opt-in
  };
  version: string;           // Consent policy version
  method: 'explicit' | 'implicit';
  ipAddress: string;         // For audit trail
  userAgent: string;
}

class ConsentManager {
  async recordConsent(consent: ConsentRecord): Promise<void> {
    await db.consents.insert({
      ...consent,
      id: generateId(),
      createdAt: new Date()
    });
    
    // Update user preferences
    await this.updateUserPreferences(consent.userId, consent.purposes);
    
    // Audit log
    await this.logConsentChange(consent);
  }
  
  async hasConsent(userId: string, purpose: string): Promise<boolean> {
    const consent = await db.consents
      .where('userId', userId)
      .orderBy('timestamp', 'desc')
      .first();
    
    return consent?.purposes[purpose] === true;
  }
  
  async withdrawConsent(userId: string, purposes: string[]): Promise<void> {
    const currentConsent = await this.getCurrentConsent(userId);
    
    purposes.forEach(purpose => {
      currentConsent.purposes[purpose] = false;
    });
    
    await this.recordConsent(currentConsent);
    
    // Trigger data deletion for withdrawn purposes
    await this.handleConsentWithdrawal(userId, purposes);
  }
}

// API endpoint for consent management
app.post('/api/privacy/consent', async (req, res) => {
  const { purposes } = req.body;
  
  await consentManager.recordConsent({
    userId: req.user.id,
    timestamp: new Date(),
    purposes,
    version: '2.0',
    method: 'explicit',
    ipAddress: req.ip,
    userAgent: req.get('user-agent')
  });
  
  res.json({ success: true });
});

3. Data Minimization and Purpose Limitation

// Collect only necessary data
interface UserRegistration {
  email: string;           // Required
  password: string;        // Required
  // DON'T collect: phone, address, birthdate unless truly necessary
}

// Purpose-specific data access
class DataAccessControl {
  async getUserData(userId: string, purpose: string): Promise<Partial<User>> {
    const user = await db.users.findById(userId);
    
    // Return only fields needed for this purpose
    switch (purpose) {
      case 'authentication':
        return { id: user.id, email: user.email };
      
      case 'billing':
        return { id: user.id, email: user.email, billingAddress: user.address };
      
      case 'analytics':
        // Return pseudonymized data
        return { id: hashUserId(user.id), country: user.country };
      
      default:
        throw new Error('Invalid data access purpose');
    }
  }
}

// Automatic field filtering
function filterPersonalData(data: any, allowedFields: string[]) {
  return Object.keys(data)
    .filter(key => allowedFields.includes(key))
    .reduce((obj, key) => {
      obj[key] = data[key];
      return obj;
    }, {});
}

4. Encryption and Pseudonymization

Encryption at Rest and in Transit

import crypto from 'crypto';

class DataProtection {
  private encryptionKey: Buffer;
  
  constructor() {
    this.encryptionKey = Buffer.from(process.env.DATA_ENCRYPTION_KEY, 'hex');
  }
  
  // Field-level encryption for sensitive data
  encryptField(plaintext: string): string {
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv('aes-256-gcm', this.encryptionKey, iv);
    
    let encrypted = cipher.update(plaintext, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    
    const authTag = cipher.getAuthTag();
    
    // Return: iv + authTag + encrypted
    return iv.toString('hex') + authTag.toString('hex') + encrypted;
  }
  
  decryptField(encrypted: string): string {
    const iv = Buffer.from(encrypted.slice(0, 32), 'hex');
    const authTag = Buffer.from(encrypted.slice(32, 64), 'hex');
    const ciphertext = encrypted.slice(64);
    
    const decipher = crypto.createDecipheriv('aes-256-gcm', this.encryptionKey, iv);
    decipher.setAuthTag(authTag);
    
    let decrypted = decipher.update(ciphertext, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    
    return decrypted;
  }
  
  // Pseudonymization for analytics
  pseudonymize(userId: string): string {
    return crypto
      .createHmac('sha256', process.env.PSEUDONYM_KEY)
      .update(userId)
      .digest('hex');
  }
}

// Database schema with encrypted fields
CREATE TABLE users (
  id UUID PRIMARY KEY,
  email VARCHAR(255) NOT NULL,
  encrypted_ssn TEXT,           -- Encrypted sensitive data
  encrypted_payment_method TEXT,
  pseudonym_id VARCHAR(64),     -- For analytics
  created_at TIMESTAMP
);

5. Data Retention and Deletion

Automated Retention Policies

// Retention policy configuration
const retentionPolicies = {
  userProfiles: { retention: 730, unit: 'days' },      // 2 years
  accessLogs: { retention: 90, unit: 'days' },
  marketingConsent: { retention: 365, unit: 'days' },
  backups: { retention: 30, unit: 'days' }
};

// Scheduled cleanup job
class DataRetentionService {
  async enforceRetentionPolicies(): Promise<void> {
    // Delete inactive user accounts
    await this.deleteInactiveUsers();
    
    // Anonymize old analytics data
    await this.anonymizeOldAnalytics();
    
    // Delete old logs
    await this.deleteOldLogs();
  }
  
  async deleteInactiveUsers(): Promise<void> {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - retentionPolicies.userProfiles.retention);
    
    const inactiveUsers = await db.users
      .where('lastLoginAt', '<', cutoffDate)
      .where('deletionScheduled', false);
    
    for (const user of inactiveUsers) {
      await this.scheduleUserDeletion(user.id);
    }
  }
  
  async anonymizeOldAnalytics(): Promise<void> {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - 90);
    
    await db.analytics
      .where('timestamp', '<', cutoffDate)
      .update({
        userId: null,
        ipAddress: '0.0.0.0',
        userAgent: 'anonymized'
      });
  }
}

// Cron job
import cron from 'node-cron';

cron.schedule('0 2 * * *', async () => {
  const service = new DataRetentionService();
  await service.enforceRetentionPolicies();
});

Right to Erasure (GDPR Article 17)

class DataDeletionService {
  async handleDeletionRequest(userId: string): Promise<void> {
    // 1. Mark account for deletion
    await db.users.update(userId, {
      deletionScheduled: true,
      deletionRequestedAt: new Date()
    });
    
    // 2. Notify downstream systems
    await this.notifyThirdParties(userId);
    
    // 3. Delete personal data (after grace period)
    await this.deleteUserData(userId);
  }
  
  private async deleteUserData(userId: string): Promise<void> {
    // Delete from all tables
    await db.transaction(async (trx) => {
      await trx('user_profiles').where('userId', userId).del();
      await trx('user_preferences').where('userId', userId).del();
      await trx('consent_records').where('userId', userId).del();
      
      // Anonymize instead of delete for audit/legal requirements
      await trx('orders').where('userId', userId).update({
        userId: null,
        email: '[email protected]',
        name: 'Deleted User'
      });
      
      // Delete from search indexes
      await searchIndex.deleteUser(userId);
      
      // Delete from cache
      await cache.del(`user:${userId}`);
      
      // Delete from backups (scheduled job)
      await this.scheduleBackupDeletion(userId);
    });
    
    // Log deletion for compliance audit
    await auditLog.log({
      action: 'DATA_DELETION',
      userId,
      timestamp: new Date(),
      reason: 'User request (GDPR Article 17)'
    });
  }
}

6. Data Subject Rights (GDPR)

Right to Access (Data Portability)

class DataExportService {
  async exportUserData(userId: string): Promise<object> {
    const [user, orders, consents] = await Promise.all([
      db.users.findById(userId),
      db.orders.where('userId', userId).select(),
      db.consents.where('userId', userId).select()
    ]);
    
    return {
      personalInformation: {
        email: user.email,
        name: user.name,
        createdAt: user.createdAt
      },
      orders: orders.map(order => ({
        id: order.id,
        date: order.createdAt,
        items: order.items,
        total: order.total
      })),
      consentHistory: consents.map(c => ({
        timestamp: c.timestamp,
        purposes: c.purposes,
        version: c.version
      })),
      generatedAt: new Date().toISOString()
    };
  }
}

// API endpoint
app.get('/api/privacy/export', requireAuth, async (req, res) => {
  const exportService = new DataExportService();
  const data = await exportService.exportUserData(req.user.id);
  
  res.setHeader('Content-Type', 'application/json');
  res.setHeader('Content-Disposition', 'attachment; filename=my-data.json');
  res.json(data);
});

7. Privacy-Enhancing Technologies

Differential Privacy for Analytics

// Add noise to aggregate queries
class DifferentialPrivacy {
  private epsilon: number = 0.1; // Privacy budget
  
  addLaplaceNoise(trueValue: number, sensitivity: number): number {
    const scale = sensitivity / this.epsilon;
    const u = Math.random() - 0.5;
    const noise = -scale * Math.sign(u) * Math.log(1 - 2 * Math.abs(u));
    return Math.round(trueValue + noise);
  }
  
  async getAggregateStats(query: string): Promise<number> {
    const trueCount = await db.raw(query);
    // Add noise to protect individual privacy
    return this.addLaplaceNoise(trueCount, 1);
  }
}

// Usage in analytics
app.get('/api/analytics/user-count', async (req, res) => {
  const dp = new DifferentialPrivacy();
  const count = await dp.getAggregateStats(
    'SELECT COUNT(*) FROM users WHERE country = ?', 
    [req.query.country]
  );
  res.json({ count });
});

8. Third-Party Data Sharing

Data Processing Agreements (DPA)

// Track third-party processors
const dataProcessors = {
  'email-service': {
    name: 'SendGrid',
    purpose: 'Transactional emails',
    dataShared: ['email', 'name'],
    dpaSignedDate: '2024-01-15',
    region: 'US',
    adequacyDecision: 'Data Privacy Framework'
  },
  'analytics': {
    name: 'Self-hosted Matomo',
    purpose: 'Website analytics',
    dataShared: ['pseudonymized user ID', 'page views'],
    dpaSignedDate: '2024-01-20',
    region: 'EU',
    adequacyDecision: 'N/A (in-house)'
  }
};

// Audit data transfers
class DataTransferAudit {
  async logTransfer(processor: string, userId: string, purpose: string) {
    await db.dataTransfers.insert({
      processor,
      userId,
      purpose,
      timestamp: new Date(),
      consentVerified: await consentManager.hasConsent(userId, purpose)
    });
  }
}

Privacy Compliance Checklist

Essential Privacy Requirements:

  • ✅ Maintain complete data inventory
  • ✅ Implement granular consent management
  • ✅ Practice data minimization
  • ✅ Encrypt sensitive data at rest and in transit
  • ✅ Enforce automated retention policies
  • ✅ Support data subject rights (access, deletion, portability)
  • ✅ Pseudonymize or anonymize where possible
  • ✅ Conduct Privacy Impact Assessments (PIA)
  • ✅ Maintain audit logs of data access
  • ✅ Have Data Processing Agreements with vendors
  • ✅ Provide transparent privacy policies
  • ✅ Train staff on privacy practices
  • ✅ Appoint Data Protection Officer (if required)
  • ✅ Document lawful basis for processing
  • ✅ Implement breach notification procedures

Conclusion

Privacy by Design isn't just about compliance—it's about building user trust through responsible data practices. By implementing these privacy engineering techniques, you create systems that respect user privacy while meeting the requirements of GDPR, CCPA, and other global regulations. Remember that privacy is an ongoing commitment requiring regular reviews, updates, and a culture of privacy awareness throughout your organization.