Access Control for Backup Systems

Access Control for Backup Systems

Backup systems require stringent access controls that differ from production systems. While production access might be necessary for daily operations, backup access should be exceptional, requiring additional authorization and leaving comprehensive audit trails. The principle of least privilege becomes even more critical when single backup sets contain data spanning multiple systems and time periods.

Segregation of duties ensures no single individual can both create and restore backups without oversight. Backup operators might create backups but lack restoration privileges. Restoration requests should require approval from data owners or security teams. Emergency access procedures must balance speed with security, perhaps using break-glass accounts that trigger immediate alerts.

// Example: Backup access control system with approval workflow
const { EventEmitter } = require('events');
const crypto = require('crypto');

class BackupAccessControl extends EventEmitter {
    constructor(config) {
        super();
        this.config = config;
        this.pendingRequests = new Map();
        this.activeAccess = new Map();
        this.auditLog = new AuditLog();
    }
    
    async requestBackupAccess(requestData) {
        const request = {
            id: crypto.randomUUID(),
            requestor: requestData.userId,
            backupId: requestData.backupId,
            purpose: requestData.purpose,
            accessType: requestData.accessType, // 'read', 'restore', 'delete'
            justification: requestData.justification,
            requestedAt: new Date(),
            status: 'pending',
            expiresAt: new Date(Date.now() + this.config.requestTimeout)
        };
        
        // Validate request
        await this.validateAccessRequest(request);
        
        // Determine required approvers
        const approvers = await this.determineApprovers(request);
        request.requiredApprovers = approvers;
        request.approvals = [];
        
        // Store pending request
        this.pendingRequests.set(request.id, request);
        
        // Notify approvers
        await this.notifyApprovers(request, approvers);
        
        // Audit log
        await this.auditLog.logAccessRequest(request);
        
        return {
            requestId: request.id,
            status: 'pending_approval',
            requiredApprovals: approvers.length,
            expiresAt: request.expiresAt
        };
    }
    
    async determineApprovers(request) {
        const approvers = [];
        
        // Base approvers by access type
        const baseApprovers = {
            read: ['security_team'],
            restore: ['data_owner', 'security_team'],
            delete: ['data_owner', 'security_team', 'compliance_officer']
        };
        
        approvers.push(...baseApprovers[request.accessType] || []);
        
        // Additional approvers based on data classification
        const backupMetadata = await this.getBackupMetadata(request.backupId);
        
        if (backupMetadata.classification === 'highly_sensitive') {
            approvers.push('ciso');
        }
        
        if (backupMetadata.containsPII) {
            approvers.push('privacy_officer');
        }
        
        if (backupMetadata.age > 365) { // Older than 1 year
            approvers.push('records_manager');
        }
        
        // Remove duplicates and filter to actual users
        const uniqueApprovers = [...new Set(approvers)];
        return this.resolveApproverUsers(uniqueApprovers);
    }
    
    async approveRequest(requestId, approverId, decision, comments) {
        const request = this.pendingRequests.get(requestId);
        
        if (!request) {
            throw new Error('Request not found or expired');
        }
        
        // Verify approver is authorized
        if (!request.requiredApprovers.includes(approverId)) {
            throw new Error('User not authorized to approve this request');
        }
        
        // Record approval
        const approval = {
            approverId,
            decision,
            comments,
            timestamp: new Date()
        };
        
        request.approvals.push(approval);
        
        // Check if all approvals received
        const approved = request.approvals.filter(a => a.decision === 'approve');
        
        if (approved.length >= request.requiredApprovers.length) {
            // All approvals received - grant access
            await this.grantAccess(request);
        } else if (decision === 'deny') {
            // Any denial rejects the request
            request.status = 'denied';
            this.pendingRequests.delete(requestId);
            await this.notifyRequestor(request, 'denied');
        }
        
        // Audit log
        await this.auditLog.logApproval(request, approval);
    }
    
    async grantAccess(request) {
        // Generate time-limited access credentials
        const accessGrant = {
            grantId: crypto.randomUUID(),
            requestId: request.id,
            userId: request.requestor,
            backupId: request.backupId,
            accessType: request.accessType,
            grantedAt: new Date(),
            expiresAt: new Date(Date.now() + this.config.accessDuration),
            credentials: await this.generateTemporaryCredentials(request)
        };
        
        // Store active access
        this.activeAccess.set(accessGrant.grantId, accessGrant);
        
        // Set up automatic revocation
        setTimeout(() => {
            this.revokeAccess(accessGrant.grantId);
        }, this.config.accessDuration);
        
        // Monitor access usage
        this.monitorAccessUsage(accessGrant);
        
        // Update request status
        request.status = 'approved';
        request.accessGrant = accessGrant;
        this.pendingRequests.delete(request.id);
        
        // Notify requestor
        await this.notifyRequestor(request, 'approved', accessGrant);
        
        // Audit log
        await this.auditLog.logAccessGrant(accessGrant);
        
        return accessGrant;
    }
    
    async generateTemporaryCredentials(request) {
        // Create credentials based on access type
        const credentials = {
            accessKey: crypto.randomBytes(32).toString('base64'),
            secretKey: crypto.randomBytes(64).toString('base64'),
            sessionToken: crypto.randomBytes(128).toString('base64'),
            permissions: this.mapAccessTypeToPermissions(request.accessType),
            restrictions: {
                backupIds: [request.backupId],
                ipWhitelist: await this.getUserIPWhitelist(request.requestor),
                validFrom: new Date(),
                validUntil: new Date(Date.now() + this.config.accessDuration)
            }
        };
        
        // Encrypt credentials for transmission
        const encryptedCreds = await this.encryptCredentials(
            credentials,
            request.requestor
        );
        
        return encryptedCreds;
    }
    
    monitorAccessUsage(accessGrant) {
        // Set up real-time monitoring
        const monitor = setInterval(async () => {
            const usage = await this.getAccessUsage(accessGrant.grantId);
            
            // Check for suspicious activity
            if (usage.downloadSize > this.config.maxDownloadSize) {
                await this.handleSuspiciousActivity(accessGrant, 'excessive_download');
            }
            
            if (usage.accessLocations.length > 1) {
                await this.handleSuspiciousActivity(accessGrant, 'multiple_locations');
            }
            
            if (usage.failedAttempts > this.config.maxFailedAttempts) {
                await this.handleSuspiciousActivity(accessGrant, 'failed_attempts');
                this.revokeAccess(accessGrant.grantId);
            }
            
        }, 60000); // Check every minute
        
        // Clean up monitor on expiration
        setTimeout(() => clearInterval(monitor), this.config.accessDuration);
    }
    
    async handleSuspiciousActivity(accessGrant, activityType) {
        // Log suspicious activity
        await this.auditLog.logSuspiciousActivity(accessGrant, activityType);
        
        // Notify security team
        await this.notifySecurityTeam({
            alert: 'Suspicious backup access activity',
            accessGrant,
            activityType,
            timestamp: new Date()
        });
        
        // Potentially revoke access based on severity
        if (this.isCriticalActivity(activityType)) {
            await this.revokeAccess(accessGrant.grantId);
        }
    }
}