Introduction

Data security is a crucial aspect of any application, and Salesforce Apex provides a powerful development framework for building robust solutions. However, Apex operates in the most privileged mode called System mode, granting unrestricted access to all data.

In this blog post, we will explore the challenges posed by this mode and discuss Salesforce’s new approach to address them.

  1. Restricting Data Access in Apex: ideally, when users interact with your Salesforce app, they should only be able to access the data they have been explicitly granted permission for. However, Apex’s default execution provides access to all data, which poses a significant security risk when the developer is not aware. To mitigate this, Salesforce has been actively working on implementing mechanisms to restrict data access.
  2. The Struggle to Degrade SYSTEM_MODE: Over the years, Salesforce has tried to degrade the execution mode from System mode to limit data accessibility. However, achieving this transition has proven challenging. Senior Director Chris Peterson and Apex Product Manager Daniel Ballinger, have shared insights into the difficulties encountered during these attempts.

If you have been using the infamous Schema.SObjectType.*.isAccessible with checking field by field with Schema.SObjectType.Account.fields.*.isAccessible, or you have used WITH SECURITY_ENFORCED or you are not aware of any of those, read this article.

This screenshots are Salesforce materials that can be found here.

New Approach

The new approach consists in not degrading on top of SYSTEM_MODE anymore, but applying the right data access to the user for every single query or DML done.

DML operations

//Using as user
insert as user new Account(Name = 'Salesforce Inc.');

//Dynamic version 
Database.insert( new Account(Name = 'Salesforce Inc.'), AccessLevel.USER_MODE);

a.Name = 'Salesforce';
update <strong>as system</strong> a;

Queries operations

Account a = [SELECT Id, Name FROM Account WHERE Name = 'Salesforce Inc.'<strong> WITH USER_MODE</strong>]

//Dynamic version
Account a = Database.query('SELECT Id, Name FROM Account WHERE Name = 'Salesforce Inc.',<strong> AccessLevel.USER_MODE</strong>)

//Also in aggregations
List<AggregateResult> groupedResults = [SELECT SUM(AMOUNT) total                                                 FROM Opportunity WHERE AccountId = :accountId
                                               WITH USER_MODE];

How contradictions are handled?

What if I use without sharing and the new model together? User mode overrides during this operation the without sharing clause.

public without sharing MyClass {
   ...
   Database.insert( new Account(Name = 'Salesforce Inc.'), AccessLevel.USER_MODE);

}

A bit inconvenient

User mode database operations generate security exceptions if a CRUD/FLS violation is found, so if you have the requirement to avoid exceptions but still enforce security then you need the Security.stringInaccessible()method.

try {
     integer count = Database.countQuery( SELECT COUNT(email) cnt FROM  Contact, AccessLevel. USER_MODE);

} catch (QueryException qe) {
     Map<String, Set<String>> inaccessibleFields = qe.getInaccessibleFields();

     System.assert (!inaccessibleFields.containsKey('Contact'), 'Missing Contact CRUD' );
     System.assert (!inaccessibleFields.get('Contact').contains ('Email'), 'Missing Contact. Email
FLS');
     System.assert (!inaccessibleFields.get('Account').contains ('Website'), 'Missing Account. Website
FLS');
}

Important

  • When the operation is finished the code come backs to SYSTEM MODE.
  • If you want to force to use system mode just use WITH SYSTEM_MODE instead of WITH USER_MODE.
  • It is recommended that you refactor your code that uses WITH SECURITY_ENFORCED to WITH USER_MODE, which is the optimized fully compatible and future-proof version.
  • You should also consider to refactor your code to simplify the sequence of Schema.SObjectType.Account.isAccessible with checking field by field with Schema.SObjectType.Account.fields.*.isAccessible.
  • New features in the future will work with this new syntax and approach, you don’t need to worry about.
  • If you are an ISV, using these new forms will provide you less alerts from the security scanner.
  • SOSL will not return any result if FLS if matched an inaccessible field.

Future options (not GA yet)

  • Grant temporary access using a Permission set
AccessLevel appEscalated = AccessLevel.USER_MODE.withPermissionSetId('OPSxx00000006xQ');

Database.insert(new Account(Name'Salesforce Inc.'), appEscalated);

Account a = Database.query('SELECT Id, Name FROM Account, AppEscalated);

Conclusions

As Apex developers, we have a responsibility to ensure the privacy and security of our users’ data. Salesforce’s recognition of the challenges posed by Apex’s System mode and their continuous efforts to address these concerns are encouraging.

I hope and it looks like that finally Salesforce has come up with the right, simplified and reasonable approach.

Links

I recommend you to follow the sequence:

  1. Videos:
  2. Slides: https://speakerdeck.com/ca_peterson/tdx22-user-mode-db-ops?slide=15
  3. Article (link retired by Salesforce): https://developer.salesforce.com/blogs/2023/05/write-simplified-and-secure-apex-with-spring-23-updates

I hope it helps.

Deja un comentario

Este sitio utiliza Akismet para reducir el spam. Conoce cómo se procesan los datos de tus comentarios.

Crea una web o blog en WordPress.com