Programmatic Security: Apex Enforcement & Secure Coding
Programmatic security covers how developers enforce security in Apex code, prevent common vulnerabilities, and securely integrate with external systems. This is CTA Objective 2.4, and the board expects you to know when declarative security falls short and how to enforce security correctly in code.
The Sharing Keyword Spectrum
Section titled “The Sharing Keyword Spectrum”Every Apex class runs in one of three sharing contexts. Choosing the wrong one is a common security vulnerability.
with sharing / without sharing / inherited sharing
Section titled “with sharing / without sharing / inherited sharing”| Keyword | Record Access | Use Case |
|---|---|---|
with sharing | Enforces the running user’s sharing rules (OWD, role hierarchy, sharing rules) | Default for most classes; user-facing logic |
without sharing | Ignores all sharing rules; sees all records | System operations, integrations, admin utilities |
inherited sharing | Inherits the sharing context of the calling class | Utility classes called from multiple contexts |
| (no keyword) | Inherits sharing context from calling class; defaults to without sharing only when used as the entry point of an Apex transaction | Avoid; explicit is always better |
with sharing for any user-facing class. inherited sharing is correct for utility classes that serve multiple callers, while without sharing requires explicit justification and narrow scope to avoid becoming a security bypass.Sharing Context Interaction
Section titled “Sharing Context Interaction”without sharing always overrides regardless of its caller’s context. This is why without sharing classes should be narrow (a single method rather than an entire service class) to limit the bypass scope.CRUD and FLS Enforcement
Section titled “CRUD and FLS Enforcement”Sharing controls which records a user sees. CRUD and FLS control what operations they can perform and which fields they can access. Apex does NOT automatically enforce CRUD or FLS; developers must do it explicitly.
Enforcement Methods Comparison
Section titled “Enforcement Methods Comparison”| Method | Enforces | Available Since | Where It Works |
|---|---|---|---|
WITH SECURITY_ENFORCED | Object CRUD + FLS (Read only) | Spring ‘20 (GA) | SOQL queries |
WITH USER_MODE | CRUD + FLS + Sharing | Spring ‘23 | SOQL + DML |
stripInaccessible() | FLS | Spring ‘20 (GA) | Apex (before DML or after query) |
Schema.describe | Manual check | Always | Apex (programmatic checks) |
Security.stripInaccessible() | FLS per operation type | Spring ‘20 (GA) | Apex |
WITH SECURITY_ENFORCED
Section titled “WITH SECURITY_ENFORCED”Appended to a SOQL query to enforce FLS on all referenced fields. If the running user lacks Read access to any field in the query, a System.QueryException is thrown.
// Enforces FLS - throws exception if user can't read any fieldList<Account> accounts = [ SELECT Name, Phone, AnnualRevenue FROM Account WHERE Industry = 'Technology' WITH SECURITY_ENFORCED];Limitations:
- Read-only enforcement (does not apply to DML)
- Throws an exception rather than stripping fields; all or nothing
- Enforces both object-level CRUD and field-level security on SOQL SELECT queries
- Does not enforce sharing (use
with sharingfor that)
WITH USER_MODE / WITH SYSTEM_MODE
Section titled “WITH USER_MODE / WITH SYSTEM_MODE”The modern approach. Available on both SOQL and DML.
// USER_MODE: Enforces CRUD + FLS + sharingList<Account> accounts = [ SELECT Name, Phone, AnnualRevenue FROM Account WHERE Industry = 'Technology' WITH USER_MODE];
// USER_MODE on DML: Enforces CRUD + FLSDatabase.insert(newAccounts, AccessLevel.USER_MODE);Database.update(existingAccounts, AccessLevel.USER_MODE);// SYSTEM_MODE: Bypasses CRUD + FLS + sharing (explicit bypass)List<Account> allAccounts = [ SELECT Name, Phone, AnnualRevenue FROM Account WITH SYSTEM_MODE];Behavior details:
- Works on both SOQL and DML (SECURITY_ENFORCED is SOQL-only)
WITH USER_MODEin inline SOQL throwsSystem.QueryExceptionfor inaccessible fields (similar toWITH SECURITY_ENFORCED)Database.querywithAccessLevel.USER_MODEmay silently strip inaccessible fields rather than throwing- Use
stripInaccessible()for consistent silent stripping across all contexts - Enforces sharing rules automatically
stripInaccessible()
Section titled “stripInaccessible()”Strips fields the running user cannot access before DML or after a query. Provides granular control over which operation type to check.
// Strip fields user can't read from query resultsList<Account> accounts = [SELECT Name, Phone, AnnualRevenue FROM Account];SObjectAccessDecision decision = Security.stripInaccessible( AccessType.READABLE, accounts);List<Account> sanitized = decision.getRecords();// sanitized records have inaccessible fields removed
// Strip fields user can't create before insertList<Account> newAccounts = new List<Account>();// ... populate accountsSObjectAccessDecision insertDecision = Security.stripInaccessible( AccessType.CREATABLE, newAccounts);insert insertDecision.getRecords();AccessType options: READABLE, CREATABLE, UPDATABLE, UPSERTABLE
Enforcement Selection Guide
Section titled “Enforcement Selection Guide”WITH USER_MODE is the modern default for both SOQL and DML, enforcing CRUD, FLS, and sharing in a single keyword. Use stripInaccessible() when silent field stripping is preferred over throwing exceptions, such as in bulk data operations.Declarative Filtering: Restriction & Scoping Rules
Section titled “Declarative Filtering: Restriction & Scoping Rules”While sharing rules grant access, these features filter access. They are declarative but directly impact how Apex queries return data.
Restriction Rules
Section titled “Restriction Rules”Restriction Rules narrow the records a user can see, even if they have sharing access via OWD, role hierarchy, or sharing rules. Available on custom objects, external objects, contracts, events, tasks, time sheets, and time sheet entries, but not on Account, Contact, Opportunity, or Case.
| Aspect | Sharing Rules | Restriction Rules |
|---|---|---|
| Effect | Grants access (additive) | Narrows access (subtractive) |
| Precedence | Evaluated first | Evaluated after sharing |
| Use Case | Open access to regional teams | Hide sensitive/VIP records from all but a few |
| Limit | 300 ownership-based + 300 criteria-based per object | 2 per object (EE/DE) or 5 per object (PE/UE) |
Scoping Rules
Section titled “Scoping Rules”Scoping Rules do not restrict access; they provide a default “scope” (filter) for the user’s view (e.g., “My Active Accounts”). Available on custom objects, Account, Case, Contact, Event, Lead, Opportunity, and Task.
- Behavior: Applies a default filter to list views, reports, and lookups. Does not remove access; only changes the default view.
- Apex: Scoping rules are enforced when Apex runs in user context via
WITH USER_MODE. UseUSING SCOPE EVERYTHINGto bypass scoping rules and return all records the user can access. - Use Case: Focusing large datasets for specific user groups without changing underlying sharing or security.
Named Credentials
Section titled “Named Credentials”Named Credentials store callout endpoint URLs and authentication details, keeping secrets out of Apex code. They are central to secure integrations.
Why Named Credentials Matter
Section titled “Why Named Credentials Matter”| Without Named Credentials | With Named Credentials |
|---|---|
| Credentials hardcoded or in Custom Settings | Credentials managed declaratively |
| Endpoint URLs in code | Endpoint URL configured in Setup |
| OAuth token management in Apex | Platform handles token lifecycle |
| Secrets in version control | Secrets stored securely by platform |
| Each developer manages auth | Auth configured once, used everywhere |
Named Credential Usage in Apex
Section titled “Named Credential Usage in Apex”// Callout using Named Credential - no auth code neededHttpRequest req = new HttpRequest();req.setEndpoint('callout:My_ERP_System/api/orders'); // Named Credential prefixreq.setMethod('GET');Http http = new Http();HttpResponse res = http.send(req);// Platform automatically adds authentication headersNamed Credential vs Remote Site Setting
Section titled “Named Credential vs Remote Site Setting”| Feature | Named Credential | Remote Site Setting |
|---|---|---|
| Stores auth details | Yes | No |
| Token management | Automatic (OAuth) | Manual (in code) |
| CSRF protection | Built-in | Manual |
| Per-user vs Named Principal | Both | N/A |
| Use case | Authenticated callouts | Unauthenticated callouts only |
Apex Crypto
Section titled “Apex Crypto”The Crypto class provides cryptographic functions for signing, encrypting, hashing, and generating MACs.
Common Crypto Use Cases
Section titled “Common Crypto Use Cases”| Operation | Method | Use Case |
|---|---|---|
| Hashing | Crypto.generateDigest('SHA-256', data) | Data integrity verification |
| HMAC | Crypto.generateMac('HmacSHA256', data, key) | Webhook signature validation |
| Encryption | Crypto.encrypt('AES256', key, iv, data) | Encrypt sensitive data in transit |
| Decryption | Crypto.decrypt('AES256', key, iv, data) | Decrypt received data |
| Digital Signature | Crypto.sign('RSA-SHA256', data, privateKey) | Sign outbound messages |
| Random | Crypto.getRandomInteger() / Crypto.generateAesKey(256) | Generate tokens, keys |
Secure Coding Practices
Section titled “Secure Coding Practices”SOQL Injection Prevention
Section titled “SOQL Injection Prevention”SOQL injection occurs when user input is concatenated directly into SOQL queries.
// VULNERABLE - user input directly in query stringString userInput = ApexPages.currentPage().getParameters().get('name');String query = 'SELECT Id, Name FROM Account WHERE Name = \'' + userInput + '\'';List<Account> accounts = Database.query(query); // SOQL INJECTION RISK
// SAFE - use bind variablesString userInput = ApexPages.currentPage().getParameters().get('name');List<Account> accounts = [SELECT Id, Name FROM Account WHERE Name = :userInput];
// SAFE - use String.escapeSingleQuotes() for dynamic SOQLString userInput = ApexPages.currentPage().getParameters().get('name');String safe = String.escapeSingleQuotes(userInput);String query = 'SELECT Id, Name FROM Account WHERE Name = \'' + safe + '\'';List<Account> accounts = Database.query(query);Cross-Site Scripting (XSS) Prevention
Section titled “Cross-Site Scripting (XSS) Prevention”XSS occurs when user-supplied data is rendered in HTML without encoding.
| Context | Prevention | Visualforce | LWC |
|---|---|---|---|
| HTML body | HTML encode | {!HTMLENCODE(value)} or <apex:outputText escape="true"/> | Automatic (template auto-escapes) |
| JavaScript | JavaScript encode | {!JSENCODE(value)} | Use properties, not innerHTML |
| URL parameters | URL encode | {!URLENCODE(value)} | Use NavigationMixin |
| CSS | CSS encode | Avoid dynamic CSS values | Avoid dynamic CSS values |
Other Secure Coding Practices
Section titled “Other Secure Coding Practices”| Vulnerability | Prevention |
|---|---|
| CSRF | Use Salesforce anti-CSRF tokens (built into VF/LWC); validate state parameter in OAuth |
| Open redirect | Validate redirect URLs against allowlist; never use user input directly |
| Insecure direct object reference | Always check CRUD/FLS/sharing before returning records |
| Sensitive data exposure | Use Shield Encryption; mask fields in UI; avoid logging sensitive data |
| Insufficient logging | Log security events; use Event Monitoring; implement Transaction Security |
Session-Based Permission Sets
Section titled “Session-Based Permission Sets”Session-based Permission Sets activate only when specific conditions are met during a user’s session. They provide conditional, elevated access.
How Session-Based Permission Sets Work
Section titled “How Session-Based Permission Sets Work”Use Cases for Session-Based Permission Sets
Section titled “Use Cases for Session-Based Permission Sets”| Scenario | Implementation |
|---|---|
| Step-up authentication | User re-authenticates to access sensitive data; session PS grants access to encrypted fields |
| Time-limited elevated access | Approval workflow activates session PS for data export during audit period |
| Location-based access | Flow checks IP range; activates session PS for internal network users |
| Role-based escalation | Manager approves temporary access; Flow activates session PS for the requesting user |
Activating Session-Based Permission Sets
Section titled “Activating Session-Based Permission Sets”| Method | How | Best For |
|---|---|---|
| Flow | SessionPermissionSetActivation element | Most scenarios; declarative |
| Auth Provider | Custom Auth Provider registration handler | SSO-triggered activation |
| Apex | SessionPermissionSetActivation record insert | Complex logic or API-driven |
| Connected App | Session-based PS assigned to Connected App | Integration-specific access |
Secure Integration Callout Architecture
Section titled “Secure Integration Callout Architecture”When designing integrations, the CTA board expects you to articulate the full security stack for callouts, not just “use Named Credentials.”
Security in LWC vs Visualforce vs Aura
Section titled “Security in LWC vs Visualforce vs Aura”| Security Aspect | LWC | Aura | Visualforce |
|---|---|---|---|
| XSS protection | Auto-escaped templates | Semi-auto ({!v.value} is safe, $A.util.format is not) | Manual (HTMLENCODE, JSENCODE) |
| CSRF protection | Built-in | Built-in | Built-in (anti-CSRF token) |
| Data access | Via Apex (enforce in Apex) | Via Apex (enforce in Apex) | Direct binding (enforce FLS in controller) |
| Namespace isolation | Shadow DOM | Locker Service | None |
| CSP enforcement | Strict | Moderate | Relaxed |
Related Topics
Section titled “Related Topics”- Sharing Model:
with sharingenforces the sharing model in code - Field & Object Security: CRUD/FLS that Apex must enforce
- Shield Encryption: encrypted field access in Apex
- Identity & SSO: Named Credentials and OAuth in integrations
- Security Best Practices: secure coding patterns and anti-patterns
- Integration: Named Credentials and callout security
Sources
Section titled “Sources”- Salesforce Help: Apex Security
- Salesforce Help: stripInaccessible
- Salesforce Help: SOQL Security Modes
- Salesforce Help: Named Credentials
- Salesforce Help: Session-Based Permission Sets
- Salesforce Developers: Secure Coding Guidelines
- OWASP: SOQL Injection Prevention
Personal study notes for the Salesforce CTA exam. Content compiled from VJ's study notes, official Salesforce documentation, community sources, and online publicly available content, then organized and presented with AI assistance. Not affiliated with Salesforce. © 2025–2026 VJ Srivastava.