mirror of
https://github.com/forcedotcom/afv-library.git
synced 2026-07-30 03:09:50 +08:00
5.9 KiB
5.9 KiB
Integration Security Best Practices
Overview
Security is critical for integrations. This guide covers best practices for securing Salesforce integrations with external systems.
Credential Management
DO: Use Named Credentials
// ✅ CORRECT - Named Credential handles auth
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:MySecureAPI/resource');
DON'T: Hardcode Credentials
// ❌ WRONG - Never hardcode credentials
req.setHeader('Authorization', 'Bearer sk_live_abc123...');
req.setEndpoint('https://api.example.com');
Credential Storage Rules
| Item | Storage Location | Never Store In |
|---|---|---|
| API Keys | Named Credential | Apex code, Custom Settings |
| Client Secrets | Named Credential / External Credential | Source control |
| Certificates | Certificate & Key Management | Static Resources |
| Passwords | Named Credential | Custom Metadata |
OAuth Best Practices
Scope Minimization
Request only necessary scopes:
✅ read:orders write:orders
❌ admin:* read:* write:*
Token Handling
- Never log access tokens
- Don't expose tokens in error messages
- Use short-lived tokens when possible
- Implement token refresh handling
PKCE for Public Clients
For mobile or SPA clients:
Use Authorization Code with PKCE, not Implicit flow
Network Security
Remote Site Settings
Template: assets/endpoint-security/example.remoteSite-meta.xml
Template: assets/endpoint-security/example.cspTrustedSite-meta.xml
- Only allow necessary domains
- Don't use wildcard domains
- Review and audit regularly
Certificate Validation
- Use trusted CA certificates
- Don't disable SSL/TLS verification
- Monitor certificate expiration
IP Restrictions
For Connected Apps:
- Configure IP relaxation carefully
- Use "Enforce IP restrictions" when possible
Input Validation
Validate External Data
// Validate before processing
public static void processExternalData(String externalId) {
// Validate format
if (!Pattern.matches('[A-Za-z0-9]{10,20}', externalId)) {
throw new ValidationException('Invalid external ID format');
}
// Sanitize for SOQL
String safeId = String.escapeSingleQuotes(externalId);
}
Output Encoding
// Encode data sent to external systems
String encodedData = EncodingUtil.urlEncode(userData, 'UTF-8');
Error Handling Security
Don't Expose Internal Details
// ❌ WRONG - Exposes internal structure
throw new CalloutException('Failed: ' + response.getBody());
// ✅ CORRECT - User-friendly, log details separately
System.debug(LoggingLevel.ERROR, 'API Error: ' + response.getBody());
throw new CalloutException('Unable to complete request. Contact support.');
Log Securely
// ❌ WRONG - Logs sensitive data
System.debug('Request: ' + JSON.serialize(request)); // May contain PII
// ✅ CORRECT - Redact sensitive fields
System.debug('Request to: ' + endpoint + ', Status: ' + statusCode);
API Security Patterns
Rate Limiting Awareness
if (response.getStatusCode() == 429) {
String retryAfter = response.getHeader('Retry-After');
// Implement backoff, don't hammer the API
}
Idempotency Keys
For POST requests that shouldn't duplicate:
req.setHeader('Idempotency-Key', generateUniqueKey());
Request Signing
For APIs requiring signature:
String signature = generateHmacSignature(payload, secretKey);
req.setHeader('X-Signature', signature);
User Context Security
Per-User vs Named Principal
| Scenario | Use |
|---|---|
| User-specific data access | Per-User Principal |
| Background/batch jobs | Named Principal |
| Service integrations | Named Principal |
| User-initiated with audit | Per-User Principal |
Audit Logging
public static void logIntegrationActivity(String operation, Id userId, String externalSystem) {
Integration_Log__c log = new Integration_Log__c(
Operation__c = operation,
User__c = userId,
External_System__c = externalSystem,
Timestamp__c = Datetime.now()
);
insert log;
}
Platform Event Security
Sensitive Data in Events
- Don't include PII in event payloads when avoidable
- Use record IDs and query for details
- Consider encryption for sensitive fields
Event Consumer Validation
trigger SecureEventHandler on My_Event__e (after insert) {
for (My_Event__e event : Trigger.new) {
// Validate event source/origin if possible
if (!isValidEventSource(event)) {
System.debug(LoggingLevel.WARN, 'Suspicious event: ' + event.ReplayId);
continue;
}
processEvent(event);
}
}
Security Checklist
Before Deployment
- Named Credentials used for all external calls
- No hardcoded credentials in code
- OAuth scopes minimized
- Remote Site Settings restricted
- Error messages don't expose internals
- Sensitive data not logged
- Input validation implemented
- Rate limiting handled
- Certificate expiration monitored
Regular Review
- Audit Named Credential usage
- Review integration user permissions
- Check for unused credentials
- Monitor integration error logs
- Validate certificate validity
- Review OAuth app authorizations
Compliance Considerations
GDPR / Data Privacy
- Minimize data transferred
- Document data flows
- Implement data deletion for integrated records
- Encrypt PII in transit and at rest
SOC 2 / Security Audits
- Maintain integration documentation
- Log all external access
- Implement change management
- Regular security assessments
HIPAA (Healthcare)
- Business Associate Agreements
- Encryption requirements
- Access logging
- Minimum necessary standard