Speaking twice at Dreamforce · Sept 15-17 →

Guide

Salesforce security and compliance. Who sees a record, and why.

An auditor will not ask which features you licensed. They will ask who can see a record, why, and who approved it.

Get the written assessment

Certified Partner since 2010 · MVP Hall of Fame · 200+ agents in production · UAE and US desks

The question an auditor actually asks

An auditor will not ask which security features you licensed. They will ask who can see a given record, why, and who approved that. If the answer lives in one admin's memory of a sharing rule written four years ago, the finding writes itself.

Salesforce gives you the controls; the work is making them describable. That means an identity story you can draw on a whiteboard, a sharing model that matches how the business is actually structured rather than how the org chart looked at go-live, field access decided per field instead of per panic, and monitoring that surfaces an unusual export while it is still happening. GDPR, HIPAA, and SOC 2 largely ask for the same evidence in different words, and the sections below map the controls to each. When we run this, we start from the record and work outward to the regime, not the other way around.

Identity and Access Management (IAM)

Single Sign-On (SSO) Implementation

SAML 2.0 Configuration

<!-- SAML Assertion Example -->
<saml2:Assertion 
    xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"
    ID="_8e8dc5f69a98cc4c1ff3427e5ce34606fd672f91e6"
    IssueInstant="2024-01-15T12:00:00Z"
    Version="2.0">
    
    <saml2:Issuer>https://idp.company.com</saml2:Issuer>
    
    <saml2:Subject>
        <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">
            user@company.com
        </saml2:NameID>
        <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
            <saml2:SubjectConfirmationData
                NotOnOrAfter="2024-01-15T12:05:00Z"
                Recipient="https://company.my.salesforce.com"/>
        </saml2:SubjectConfirmation>
    </saml2:Subject>
    
    <saml2:AuthnStatement AuthnInstant="2024-01-15T12:00:00Z">
        <saml2:AuthnContext>
            <saml2:AuthnContextClassRef>
                urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
            </saml2:AuthnContextClassRef>
        </saml2:AuthnContext>
    </saml2:AuthnStatement>
</saml2:Assertion>

Multi-Factor Authentication (MFA) Implementation

public class MFAEnforcementHandler {
    
    public static void enforceHighAssuranceSession(Id userId, String context) {
        // Check if user has high assurance session
        AuthSession currentSession = [
            SELECT Id, SessionSecurityLevel, LoginType, CreatedDate
            FROM AuthSession
            WHERE UsersId = :userId
            AND IsCurrent = true
            LIMIT 1
        ];
        
        if (currentSession.SessionSecurityLevel != 'HIGH_ASSURANCE') {
            // Require step-up authentication
            throw new InsufficientPrivilegesException(
                'This operation requires multi-factor authentication. ' +
                'Please complete MFA verification to continue.'
            );
        }
        
        // Log high-value transaction
        Security_Audit_Log__c auditLog = new Security_Audit_Log__c(
            User__c = userId,
            Action__c = context,
            Session_Security_Level__c = currentSession.SessionSecurityLevel,
            IP_Address__c = Auth.SessionManagement.getCurrentSession().SourceIp,
            Timestamp__c = DateTime.now()
        );
        insert auditLog;
    }
    
    // Implement risk-based authentication
    public static Boolean requiresStepUpAuth(String operation, Decimal transactionValue) {
        User currentUser = [
            SELECT Id, Profile.Name, LastLoginDate, LoginHistory__c
            FROM User
            WHERE Id = :UserInfo.getUserId()
        ];
        
        // Risk scoring logic
        Integer riskScore = 0;
        
        // High-value transaction
        if (transactionValue > 100000) {
            riskScore += 30;
        }
        
        // Sensitive operation
        if (isSensitiveOperation(operation)) {
            riskScore += 25;
        }
        
        // Unusual login pattern
        if (isUnusualLoginPattern(currentUser)) {
            riskScore += 20;
        }
        
        // New device or location
        if (isNewDeviceOrLocation()) {
            riskScore += 25;
        }
        
        return riskScore >= 50;
    }
}

Data Protection and Encryption

Platform Shield Implementation

Field-Level Encryption

public class EncryptionManager {
    
    // Encrypted field handling
    public static void processEncryptedData(List<Account> accounts) {
        // Check if user has permission to view encrypted data
        Boolean canViewEncrypted = FeatureManagement.checkPermission(
            'CanViewEncryptedData'
        );
        
        if (!canViewEncrypted) {
            // Mask sensitive data
            for (Account acc : accounts) {
                acc.Tax_ID__c = '***-**-****';
                acc.Bank_Account__c = '****' + 
                    acc.Bank_Account__c.substring(acc.Bank_Account__c.length() - 4);
            }
        }
        
        // Log access to encrypted data
        logEncryptedDataAccess(accounts);
    }
    
    // Implement deterministic encryption for searching
    public static String getDeterministicHash(String value, String context) {
        // Use HMAC for deterministic encryption
        Blob key = Crypto.generateDigest('SHA256', 
            Blob.valueOf(UserInfo.getOrganizationId() + context)
        );
        
        Blob hmac = Crypto.generateMac('HmacSHA256', 
            Blob.valueOf(value), 
            key
        );
        
        return EncodingUtil.convertToHex(hmac);
    }
    
    // Custom encryption for attachments
    public static void encryptAttachment(Attachment att) {
        // Generate encryption key
        Blob key = Crypto.generateAesKey(256);
        
        // Encrypt attachment body
        Blob encryptedBody = Crypto.encryptWithManagedIV(
            'AES256',
            key,
            att.Body
        );
        
        // Store encrypted body
        att.Body = encryptedBody;
        
        // Store key in secure custom setting
        Encryption_Key__c keyRecord = new Encryption_Key__c(
            Name = att.Id,
            Key__c = EncodingUtil.base64Encode(key),
            Algorithm__c = 'AES256',
            Created_Date__c = DateTime.now()
        );
        insert keyRecord;
    }
}

Data Masking and Anonymization

public class DataMaskingService {
    
    public static void maskSensitiveData(List<Contact> contacts, String purpose) {
        Map<String, MaskingRule> maskingRules = getMaskingRules(purpose);
        
        for (Contact c : contacts) {
            // Email masking
            if (maskingRules.containsKey('Email')) {
                c.Email = maskEmail(c.Email);
            }
            
            // Phone masking
            if (maskingRules.containsKey('Phone')) {
                c.Phone = maskPhone(c.Phone);
            }
            
            // SSN masking
            if (maskingRules.containsKey('SSN__c')) {
                c.SSN__c = maskSSN(c.SSN__c);
            }
            
            // Address masking
            if (maskingRules.containsKey('Address')) {
                c.MailingStreet = 'REDACTED';
                c.MailingCity = c.MailingCity.substring(0, 1) + '***';
                c.MailingPostalCode = c.MailingPostalCode.substring(0, 3) + '**';
            }
        }
    }
    
    private static String maskEmail(String email) {
        if (String.isBlank(email)) return email;
        
        Integer atIndex = email.indexOf('@');
        if (atIndex <= 1) return '***@***.com';
        
        String localPart = email.substring(0, atIndex);
        String domain = email.substring(atIndex);
        
        if (localPart.length() <= 3) {
            return '***' + domain;
        } else {
            return localPart.substring(0, 2) + 
                   '***' + 
                   localPart.substring(localPart.length() - 1) + 
                   domain;
        }
    }
    
    // GDPR-compliant data anonymization
    public static void anonymizeRecords(List<Id> recordIds, String objectType) {
        // Create anonymization job
        Data_Anonymization_Job__c job = new Data_Anonymization_Job__c(
            Object_Type__c = objectType,
            Record_Count__c = recordIds.size(),
            Status__c = 'In Progress',
            Started_At__c = DateTime.now()
        );
        insert job;
        
        // Process in batch for large datasets
        Database.executeBatch(
            new DataAnonymizationBatch(recordIds, objectType, job.Id),
            200
        );
    }
}

Compliance Framework Implementation

GDPR Compliance

Data Subject Rights Implementation

public class GDPRComplianceService {
    
    // Right to Access (Article 15)
    public static DataSubjectReport generateDataSubjectReport(String email) {
        DataSubjectReport report = new DataSubjectReport();
        
        // Find all records related to the data subject
        List<Contact> contacts = [
            SELECT Id, Name, Email, Phone, CreatedDate, LastModifiedDate,
                   (SELECT Id, Subject, CreatedDate FROM Cases),
                   (SELECT Id, Subject, CreatedDate FROM Tasks)
            FROM Contact
            WHERE Email = :email
        ];
        
        List<Lead> leads = [
            SELECT Id, Name, Email, Phone, Company, CreatedDate
            FROM Lead
            WHERE Email = :email
        ];
        
        // Compile comprehensive report
        report.contacts = contacts;
        report.leads = leads;
        report.dataCollectionDate = System.now();
        
        // Include data from integrated systems
        report.externalSystemData = getExternalSystemData(email);
        
        // Generate PDF report
        PageReference reportPage = Page.GDPRDataSubjectReport;
        reportPage.getParameters().put('email', email);
        Blob reportPdf = reportPage.getContentAsPDF();
        
        // Audit log
        createGDPRAuditLog('Data Access Request', email, 'Completed');
        
        return report;
    }
    
    // Right to Erasure (Article 17)
    public static void processErasureRequest(String email, String reason) {
        // Validate request
        if (!isValidErasureRequest(email, reason)) {
            throw new GDPRException('Erasure request does not meet criteria');
        }
        
        // Find all related records
        List<Contact> contactsToAnonymize = [
            SELECT Id FROM Contact WHERE Email = :email
        ];
        
        List<Lead> leadsToDelete = [
            SELECT Id FROM Lead 
            WHERE Email = :email 
            AND IsConverted = false
        ];
        
        // Anonymize contacts (preserve for legal obligations)
        for (Contact c : contactsToAnonymize) {
            c.FirstName = 'GDPR';
            c.LastName = 'Anonymized';
            c.Email = 'anonymized_' + Crypto.getRandomInteger() + '@gdpr.invalid';
            c.Phone = null;
            c.MailingStreet = null;
            c.MailingCity = null;
            c.MailingPostalCode = null;
            c.Birthdate = null;
            // Add to suppression list
            c.GDPR_Anonymized__c = true;
            c.GDPR_Anonymization_Date__c = System.now();
        }
        
        update contactsToAnonymize;
        delete leadsToDelete;
        
        // Notify integrated systems
        publishErasureEvent(email);
        
        // Audit log
        createGDPRAuditLog('Erasure Request', email, 'Completed');
    }
    
    // Data Portability (Article 20)
    public static String exportDataSubjectData(String email, String format) {
        // Collect all data
        Map<String, List<sObject>> allData = new Map<String, List<sObject>>();
        
        allData.put('Contacts', [
            SELECT Id, Name, Email, Phone, Title, Department, 
                   CreatedDate, LastModifiedDate
            FROM Contact 
            WHERE Email = :email
        ]);
        
        allData.put('Cases', [
            SELECT Id, CaseNumber, Subject, Description, Status, 
                   CreatedDate, ClosedDate
            FROM Case 
            WHERE ContactEmail = :email
        ]);
        
        allData.put('Activities', [
            SELECT Id, Subject, Description, ActivityDate, Status
            FROM Task
            WHERE Who.Email = :email
        ]);
        
        // Export in requested format
        String exportData;
        if (format == 'JSON') {
            exportData = JSON.serializePretty(allData);
        } else if (format == 'CSV') {
            exportData = convertToCSV(allData);
        } else if (format == 'XML') {
            exportData = convertToXML(allData);
        }
        
        // Create secure download link
        String downloadUrl = createSecureDownloadLink(exportData, email);
        
        return downloadUrl;
    }
}

HIPAA Compliance

public class HIPAAComplianceService {
    
    // Implement minimum necessary access
    public static List<Patient__c> getPatientRecords(
        Set<Id> patientIds, 
        String accessContext
    ) {
        // Verify user has appropriate access
        if (!hasHIPAAAccess(UserInfo.getUserId(), accessContext)) {
            throw new HIPAAException('Access denied - HIPAA minimum necessary rule');
        }
        
        // Determine fields based on access context
        String query = 'SELECT Id, Name, MRN__c';
        
        if (accessContext == 'Treatment') {
            query += ', Diagnosis__c, Medications__c, Allergies__c';
        } else if (accessContext == 'Billing') {
            query += ', Insurance_Provider__c, Policy_Number__c';
        } else if (accessContext == 'Operations') {
            query += ', Admission_Date__c, Discharge_Date__c';
        }
        
        query += ' FROM Patient__c WHERE Id IN :patientIds';
        
        // Log access for audit
        createHIPAAAuditLog(patientIds, accessContext);
        
        return Database.query(query);
    }
    
    // Implement break-glass access
    public static void grantEmergencyAccess(Id userId, Id patientId, String reason) {
        // Validate emergency
        if (String.isBlank(reason) || reason.length() < 50) {
            throw new HIPAAException(
                'Emergency access requires detailed justification'
            );
        }
        
        // Create temporary permission
        Emergency_Access__c emergencyAccess = new Emergency_Access__c(
            User__c = userId,
            Patient__c = patientId,
            Reason__c = reason,
            Granted_At__c = DateTime.now(),
            Expires_At__c = DateTime.now().addHours(4),
            Status__c = 'Active'
        );
        insert emergencyAccess;
        
        // Notify compliance team
        notifyComplianceTeam(emergencyAccess);
        
        // Create prominent audit entry
        HIPAA_Audit_Log__c audit = new HIPAA_Audit_Log__c(
            User__c = userId,
            Patient__c = patientId,
            Action__c = 'EMERGENCY_ACCESS_GRANTED',
            Reason__c = reason,
            Timestamp__c = DateTime.now(),
            IP_Address__c = Auth.SessionManagement.getCurrentSession().SourceIp,
            Alert_Compliance__c = true
        );
        insert audit;
    }
}

Security Monitoring and Incident Response

Real-Time Security Monitoring

Event Monitoring Implementation

public class SecurityMonitoringService {
    
    @InvocableMethod(label='Process Security Event')
    public static void processSecurityEvent(List<SecurityEventRequest> requests) {
        List<Security_Alert__c> alertsToCreate = new List<Security_Alert__c>();
        
        for (SecurityEventRequest request : requests) {
            // Analyze event severity
            SecurityThreatAnalysis analysis = analyzeSecurityThreat(request);
            
            if (analysis.threatLevel >= 7) {
                // High severity - immediate action
                Security_Alert__c alert = new Security_Alert__c(
                    Event_Type__c = request.eventType,
                    Severity__c = 'Critical',
                    User__c = request.userId,
                    Description__c = analysis.description,
                    Threat_Score__c = analysis.threatLevel,
                    Status__c = 'Active',
                    Response_Required__c = true
                );
                alertsToCreate.add(alert);
                
                // Immediate response actions
                if (analysis.requiresAccountLock) {
                    lockUserAccount(request.userId, analysis.reason);
                }
                
                if (analysis.requiresSessionTermination) {
                    terminateAllSessions(request.userId);
                }
            }
        }
        
        if (!alertsToCreate.isEmpty()) {
            insert alertsToCreate;
            
            // Notify security team
            notifySecurityTeam(alertsToCreate);
        }
    }
    
    // Anomaly detection
    public static SecurityThreatAnalysis analyzeSecurityThreat(
        SecurityEventRequest event
    ) {
        SecurityThreatAnalysis analysis = new SecurityThreatAnalysis();
        analysis.threatLevel = 0;
        
        // Failed login attempts
        if (event.eventType == 'LOGIN_FAILURE') {
            Integer recentFailures = [
                SELECT COUNT()
                FROM LoginHistory
                WHERE UserId = :event.userId
                AND Status = 'Failed'
                AND LoginTime >= :DateTime.now().addMinutes(-30)
            ];
            
            if (recentFailures >= 5) {
                analysis.threatLevel = 8;
                analysis.requiresAccountLock = true;
                analysis.reason = 'Multiple failed login attempts';
            }
        }
        
        // Unusual data access patterns
        if (event.eventType == 'REPORT_EXPORT') {
            Integer recentExports = [
                SELECT COUNT()
                FROM ReportExportEvent
                WHERE UserId = :event.userId
                AND CreatedDate >= :DateTime.now().addHours(-1)
            ];
            
            if (recentExports > 10) {
                analysis.threatLevel = 7;
                analysis.description = 'Unusual volume of report exports';
            }
        }
        
        // Privilege escalation attempts
        if (event.eventType == 'PERMISSION_SET_ASSIGNMENT') {
            if (isSensitivePermissionSet(event.details)) {
                analysis.threatLevel = 9;
                analysis.requiresSessionTermination = true;
                analysis.description = 'Unauthorized privilege escalation attempt';
            }
        }
        
        return analysis;
    }
    
    // Automated incident response
    public static void executeIncidentResponse(Id alertId) {
        Security_Alert__c alert = [
            SELECT Id, Event_Type__c, User__c, Severity__c, Response_Plan__c
            FROM Security_Alert__c
            WHERE Id = :alertId
        ];
        
        Incident_Response__c response = new Incident_Response__c(
            Alert__c = alertId,
            Started_At__c = DateTime.now(),
            Status__c = 'In Progress'
        );
        insert response;
        
        try {
            // Execute response plan
            switch on alert.Event_Type__c {
                when 'DATA_BREACH' {
                    handleDataBreachResponse(alert);
                }
                when 'MALWARE_DETECTION' {
                    handleMalwareResponse(alert);
                }
                when 'UNAUTHORIZED_ACCESS' {
                    handleUnauthorizedAccessResponse(alert);
                }
                when else {
                    handleGenericSecurityResponse(alert);
                }
            }
            
            response.Status__c = 'Completed';
            response.Completed_At__c = DateTime.now();
            
        } catch (Exception e) {
            response.Status__c = 'Failed';
            response.Error_Message__c = e.getMessage();
        }
        
        update response;
    }
}

// Platform Event for real-time monitoring
trigger SecurityEventTrigger on Security_Event__e (after insert) {
    List<SecurityEventRequest> requests = new List<SecurityEventRequest>();
    
    for (Security_Event__e event : Trigger.new) {
        SecurityEventRequest request = new SecurityEventRequest();
        request.eventType = event.Event_Type__c;
        request.userId = event.User_Id__c;
        request.details = event.Event_Details__c;
        request.timestamp = event.CreatedDate;
        requests.add(request);
    }
    
    // Process asynchronously
    System.enqueueJob(new SecurityEventProcessor(requests));
}

Security Metrics Dashboard

public class SecurityMetricsService {
    
    @AuraEnabled(cacheable=true)
    public static SecurityDashboardData getSecurityMetrics() {
        SecurityDashboardData data = new SecurityDashboardData();
        
        // Authentication metrics
        data.authMetrics = new AuthenticationMetrics();
        data.authMetrics.totalLogins = [
            SELECT COUNT()
            FROM LoginHistory
            WHERE LoginTime = THIS_MONTH
        ];
        
        data.authMetrics.failedLogins = [
            SELECT COUNT()
            FROM LoginHistory
            WHERE LoginTime = THIS_MONTH
            AND Status = 'Failed'
        ];
        
        data.authMetrics.mfaAdoption = calculateMFAAdoption();
        
        // Access metrics
        data.accessMetrics = new AccessMetrics();
        data.accessMetrics.privilegedUsers = [
            SELECT COUNT()
            FROM User
            WHERE Profile.Name IN ('System Administrator', 'Security Admin')
            AND IsActive = true
        ];
        
        // Threat metrics
        data.threatMetrics = new ThreatMetrics();
        data.threatMetrics.activeThreats = [
            SELECT COUNT()
            FROM Security_Alert__c
            WHERE Status__c = 'Active'
            AND CreatedDate = THIS_MONTH
        ];
        
        data.threatMetrics.resolvedThreats = [
            SELECT COUNT()
            FROM Security_Alert__c
            WHERE Status__c = 'Resolved'
            AND CreatedDate = THIS_MONTH
        ];
        
        // Compliance metrics
        data.complianceMetrics = calculateComplianceMetrics();
        
        return data;
    }
    
    private static ComplianceMetrics calculateComplianceMetrics() {
        ComplianceMetrics metrics = new ComplianceMetrics();
        
        // Calculate compliance scores
        metrics.gdprScore = calculateGDPRCompliance();
        metrics.hipaaScore = calculateHIPAACompliance();
        metrics.soc2Score = calculateSOC2Compliance();
        
        // Identify gaps
        metrics.complianceGaps = identifyComplianceGaps();
        
        return metrics;
    }
}

The brief

Send the messy version.

The honest no: if the work is a software factory with a named delivery date, we are the wrong partner.