Writing secure Apex with the latest security best practices

Over the last few years, there have been a lot of changes around enforcing the CRUD (create, read, update, and delete) and FLS (field-level security) securities in Apex.

Writing secure Apex with the latest security best practices
Table of contents
Hutte Expert Panel

Hutte Expert Panel

Here are the experts we collaborated with to bring you unique insights.


Article Summary Box

Article Highlights

  • The use of WITH USER_MODE and WITH SYSTEM_MODE in SOQL and SOSL queries allows developers to precisely enforce CRUD, FLS, and record-level sharing security constraints.
  • Salesforce's introduction of user mode database operations represents a significant shift, enabling Apex to run in the user's context, thereby ensuring that security settings like CRUD and FLS are adhered to by default.
  • The new AccessLevel parameter in the database and search class methods enhances security customization, supporting more granular control over data access and manipulation in Apex.

The diagram above depicts the evolution of Apex security best practices. It dates back to 2006 when Salesforce developers used to do manual checks on objects to check for CRUD/FLS permission.

🔏
In 2019, Salesforce released some significant updates (“WITH SECURITY_ENFORCED” and “Security.StripInaccessible()”) to improve on some of the shortcomings of the earlier approaches.

To address most (if not all) of the issues encountered in the past, Salesforce announced user mode database operations.

Let’s see what it is all about.

Apex, by default, runs in a system mode with elevated permissions, meaning that developers can bypass security controls, such as CRUD, FLS, or record sharing, when writing code.

With user mode database operations, developers can run Apex in the user’s context and enforce the user’s CRUD/FLS and sharing rules access.

Enforce CRUD/FLS and sharing rules in SOQL

🔑
You can set the mode of the operation by using the keyword “WITH USER_MODE” for the user mode and “WITH SYSTEM_MODE” for the system mode in your SOQL (Salesforce object query language) inquiry.

Static SOQL example

Account[] accList = [
    SELECT 
    Id, Name
    FROM Account 
    WITH USER_MODE
]; // User Mode SOQL

Account[] accList = [
    SELECT 
    Id, Name
    FROM Account 
    WITH SYSTEM_MODE
]; // System Mode SOQL
Using the keyword “WITH USER_MODE,” the query respects the CRUD, FLS, and record level sharing security constraints.

Dynamic SOQL example

String query = 'SELECT Id, Name FROM Account';
Account[] accList = Database.query(query, AccessLevel.USER_MODE);

A database class has new overloaded methods, which now support an “AccessLevel” parameter.

Enforce CRUD/FLS and sharing rules for SOSL

🗣️
“WITH USER_MODE” or “WITH SYSTEM_MODE” are also supported for SOSL (Salesforce object search language) statements.

Static SOSL example

String keyword = 'Test';
List<List<SObject>> searchResults = [ 
    FIND :keyword
    IN Name FIELDS
    RETURNING
    Account(Name), 
    Contact(LastName, Account.Name)
    WITH USER_MODE
];

Dynamic SOSL example

String query = 'FIND \'Test*\' IN ALL FIELDS RETURNING Account(Name), Contact, Lead'; 
List<List<SObject>> searchResults = Search.query(query, AccessLevel.USER_MODE);  
Like the database class, a search class also has new overloaded methods, which now support an “AccessLevel” parameter.

Enforce CRUD/FLS and sharing rules for DML

🖥️
Database operations can specify user or system modes using the keywords as a user or system.
//Creating an account in user mode.
insert as user new Account(
   Name = 'Test Account'
);

//Creating an account in System mode.
insert as system new Account(
   Name = 'Test Account'
); 

For Dynamic DML, developers can use the “AccessLevel” parameter to run database operations in the user or system mode.

Database.insert(
    new Account(
        Name = 'Test Account'
    ),
    true, /*All or Nothing*/
    AccessLevel.USER_MODE
);

Important considerations

  • User mode DML operations now generate the correct DML exception. Previously, they generated a security exception. This behavior is versioned, and the valid exception is generated in API version 58.0 and later.
  • A SOQL query using the “WITH USER_MODE” keyword supports restriction and scoping rules instead of the “WITH SECURITY_ENFORCED” keyword.
  • A user mode overrides the class-level setting for the SOQL query or DML written in the mode.
public without sharing Example {
   public Account[] getAccounts() {
      String query = 'SELECT Id FROM Account';
      return Database.query(query, AccessLevel.USER_MODE);
   }
}
  • In the example above, even though the Apex class is set to run in the system context (without sharing the keyword), the SOQL query runs in the user mode, enforcing security.
  • Salesforce has recommended using the new user mode database ops instead of “WITH SECURITY_ENFORCED” going forward. "WITH SECURITY_ENFORCED" may likely be versioned out at some point.

Secure your APEX best practices

The new Salesforce release introduced numerous exciting updates for Apex, and we can anticipate even more enhancements in the upcoming Summer 2023 release.

Check out the 2023 release notes for a comprehensive overview of all the new Apex features.

🗺️
Furthermore, several exciting additions are planned for the Apex roadmap, so stay tuned for even more innovative things to come!

Spring Release Notes Box
Spring Release Updates
Enhanced Apex Capabilities
The Spring Release has introduced significant enhancements to Apex, providing developers with new tools and functionalities that support more secure and efficient coding practices.
Updated Migrate to Flow Tool
This tool has been updated to support the partial migration of most actions, excluding invocable actions. This enhancement aids developers in transitioning legacy Apex code to the more modern and robust Flow Builder, which can lead to more secure and maintainable codebases.
New and Changed Aura Components
There have been adjustments to the lightning:navigation component in Aura that affect how navigation works post-record creation, which can be crucial for maintaining secure user experiences in Salesforce applications.