In this session we will talk about Apex security in Salesforce which developer must know. We’ll go through the main concepts that developers must understand to code secure applications on the Salesforce Platform. We will cover how to enforce data security and how to prevent SOQL injection attacks in Apex. We’ll also review what Locker Service does for you and security best practices for LWC. We will also cover some security in Salesforce interview questions for apex.
We will not talk about how to configure security in Salesforce. If you want to learn about who see what check this post. When you use Apex, the security of your code is critical. You’ll need to add user permissions for Apex classes and enforce sharing rules.
User Mode vs System Mode
System mode means running apex code by ignoring user’s permissions. User mode means running apex code by respecting user’s permissions and sharing of records. Let’s understand it in more detail
Object and Field-level Security (CRUD & FLS)
Here is CRUD & FLS Data Model
Enforcing Object & FLS Permissions in Apex
Apex doesn’t enforce object-level and field-level permissions by default. Let see how we can enforce the CRUD & FLS in Apex.
Read data (SOQL) | Modify data (DML) | |
Schema methods | Yes | Yes |
WITH SECURITY_ENFORCED | Yes | No |
Security.stripInaccessible() | Yes | Yes |
Database operations in user mode (pilot) | Yes | Yes |
Schema Methods
You can also enforce object-level and field-level permissions in your code by explicitly calling the sObject describe result methods.
CRUD
you can call the isAccessible, isCreateable, or isUpdateable methods of Schema.DescribeSObjectResult
to verify whether the current user has read, create, or update access to an sObject.
- Schema.sObjectType.Contact.isAccessible() – before querying
- Schema.sObjectType.Contact.isCreateable() – before inserting
- Schema.sObjectType.Contact.isUpdateable() – before updating
- Schema.sObjectType.Contact.isDeletable() – before deleting
if (Schema.sObjectType.Contact.isDeletable()) {
// Delete Contact
}
FLS (CRUD Implicit)
You can use below method to check field level security.
- Schema.sObjectType.Contact.fields.Status__c.isAccessible() – before querying
- Schema.sObjectType.Contact.fields.Status__c.isCreateable() – before inserting
- Schema.sObjectType.Contact.fields.Status__c.isUpdateable() – before updating
if (Schema.sObjectType.Contact.fields.Email.isAccessible()) {
Contact c = [SELECT Email FROM Contact WHERE Id= :Id];
}
WITH SECURITY_ENFORCED
Use the WITH SECURITY_ENFORCED clause to enable field- and object-level security permissions checking for SOQL SELECT queries in Apex code.
It Checks for both CURF & FLS.
List<Account> acctList = [SELECT Id,
(SELECT LastName FROM Contacts)
FROM Account
WHERE Name like 'Acme'
WITH SECURITY_ENFORCED]
Security.stripInaccessible()
Use the stripInaccessible
method to enforce field- and object-level data protection. This method can be used to strip the fields and relationship fields from query and subquery results that the user can’t access. The method can also be used to remove inaccessible sObject fields before DML operations to avoid exceptions and to sanitize sObjects that have been deserialized from an untrusted source
The Id field is never stripped
SObjectAccessDecision securityDecision = Security.stripInaccessible(AccessType, sourceRecords);
Contact ctc = securityDecision.getRecords()[0];
System.debug(ctc.isSet('social_security_number__c')); // prints "false"
User mode database operations (Pilot)
sfdc.co/user-mode-dml
CRUD, FLS and sharing New parameter on:
- Database.query methods
- Search.query methods
- Database DML methods (insert, update, upsert, merge, delete, undelete, convertLead)
Apex Recipes
github.com/trailheadapps/apex-recipes
Record-level Security (Sharing)
Record Sharing Data Model
Sharing Clauses for Apex Classes
Apex generally runs in system context means current user’s permissions and field-level security take in place during code execution. Our Apex code should not expose the sensitive data to User which is hidden via security and sharing settings. Hence, Apex security and enforcing the sharing rule is most important. Let see how we can enforce the sharing in Apex.
Not related to CRUD or FLS!!!!
Sharing Enforced | |
with sharing | Yes |
without sharing | No |
inherited sharing | Inherited from parent, with sharing if entry point |
no sharing clause | Inherited from parent, without sharing if entry point except for lightning |
With Sharing Keyword
If you use this keyword, then the Apex code will enforce the Sharing settings of current user to Apex code. This does not enforce the Profile permission, only the data level sharing settings.
Without Sharing Keyword
Class declared with this keyword executes in System mode.
Enforcing the current user’s sharing rules can impact:
- SOQL and SOSL queries. A query may return fewer rows than it would operating in system context.
- DML operations. An operation may fail because the current user doesn’t have the correct permissions
System.runAs()
Only for Test Mode
Enforces sharing, not CRUD or FLS
@isTest
private class TestRunAs {
public static testMethod void testRunAs() {
// Setup test data
// Create a unique UserName
String uniqueUserName = 'standarduser' + DateTime.now().getTime() + '@testorg.com'; // This code runs as the system user
Profile p = [SELECT Id FROM Profile WHERE Name='Standard User'];
User u = new User(Alias = 'standt', Email='[email protected]', EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US', LocaleSidKey='en_US', ProfileId = p.Id,
TimeZoneSidKey='America/Los_Angeles', UserName=uniqueUserName);
System.runAs(u) {
// The following code runs as user 'u'
System.debug('Current User: ' + UserInfo.getUserName());
System.debug('Current Profile: ' + UserInfo.getProfileId());
}
}
}
Data Security in LWC
LWC Base Components enforce CRUD, FLS and Sharing
- lightning-record-form
- lightning-record-edit-form
- lightning-record-view-form
LDS Wire adapters and functions enforce CRUD, FLS and Sharing If calling Apex → apply the techniques just seen!
Application Security
SOQL Injection
When queries are built directly with user data inlined or query text, instead of using type-safe bind parameters, malicious input may be able to change the structure of the query to bypass or change application logic. This is called as SOQL injection attack.
Preventing SOQL Injection
There following way available in Salesforce to prevent the SOQL Injections.
- Use static queries
- If need to use dynamic queries, always bind user input with “:”
- If not possible, escape, typecast or whitelist inputs
1 ) Using static SOQL:
2) If need to use dynamic queries, always bind user input with “:”
public static List<Account> getAccount(String searchValue) {
String likeValue = '%' + searchValue + '%';
return (List<Account>) Database.query(
'SELECT Name FROM Account WHERE Name LIKE :likeValue'
);
}
3) Escape Single Quotes / Typecasting
If not possible, escape, typecast or whitelist inputs
String query = 'Select Name From Account Where Name like \'%'+String.escapeSingleQuotes(name)+'%\'';
Typecasting like below
String query = 'Select Name, Address from Object__c where isActive = ' + Boolean(input);
XSS (Cross-Site Scripting)
- Reflected XSS → app echoes malicious script from user input
- Stored XSS → app returns malicious script from database
- DOM XSS → reflected XSS but just on the browser
Locker Service
- JavaScript Strict mode enforcement
- DOM access containment → Safe Harbour: mechanism to relax this restriction
- Secure wrappers → sfdc.co/locker-api-viewer
- CSP → sfdc.co/locker-csp
CSRF (Cross-Site Request Forgery)
Default protection that includes a token on each resource request => validated on the server. Additionally, locker will block specific endpoints from being accessed with XHR.
As a developer:
- Don’t change state (execute DML) on component load – connectedCallback, renderedCallback….
- Validate origin header on your exposed API endpoints or add custom tokens for CSRF protection
Recording
Please subscribe our YouTube channel to get notification for video upload.
Very useful
Thank you so much very useful .You never disappointed …..
Glad you learn about Apex Security in Salesforce
no sharing clause Inherited from parent,
without sharing if entry point except for lightning
Please explain what is exactly means of entry point except lightning..does it mean if we call a class from lwc for a private obj will it fetch only the records which user has access if no clause mentioned in apex
@Amit It is very helpful and everyone should know about security in Salesforce. Thank you so much for sharing the Knowledge. Need some more explanation on With Sharing and Without Sharing concepts.