When you are writing code in Apex, the security of your code is of utmost importance. You should ensure that your code meets the latest Security guidelines. In this blog we will talk about these guidelines which any Salesforce developer working with Apex should know to code securely on the platform.
User Mode vs System Mode?
User Mode is when apex code is running by taking the user’s permissions and sharing into consideration. Profile permissions, Field level permissions, and sharing rules are respected when code is running in user mode.
System Mode is when apex code is running without taking the user’s permissions and sharing into consideration. In system mode apex code has access to all objects, fields, and records and user’s sharing, FLS, and Profile permissions are not respected.
Enforce Sharing Rules
Apex code runs in system mode by default which means current logged in user’s permissions, and field-level security are not respected during execution. To ensure that sharing rules are not enforced, class must be declared using without sharing keyword. To enforce the sharing rules, declare the class as with sharing. Inherited sharing keyword is used to run the class in the sharing mode of the class that called it.
With Sharing– With sharing enforces record permissions. It enforces the sharing rules of the current user. Setting a class as with sharing does not enforce object or field-level security permissions!
Without Sharing– Classes declared with this keyword is executed in system mode. Sharing rules for the current user are not enforced when class is declared with this keyword.
Inherited Sharing– To inherit the sharing settings from the parent class (the class that calls it), declare the class with Inherited Sharing keyword.
- One should always specify a sharing clause.
- If a class is the entry point into an Apex transaction, not specifying the sharing clause defaults to without sharing.
- If a class is the entry point into an Apex transaction, specifying inherited sharing defaults to with sharing.
- The sharing setting of the class where a method is defined is applied, not of the class where the method is called from.
- Classes inherit sharing setting from a parent class when one class extends or implements another.
Enforce Object & Field Permissions –
Apex doesn’t enforce object-level and field-level permissions by default. Object permissions & FLS of running user can be enforced by calling the sObject describe result methods (of Schema.DescribeSObjectResult) and the field describe result method (of Schema.DescribeFieldResult)
Using these methods, we can verify whether the logged in user has permissions to perform the DML or query.
Enforcing these methods require more work on your part as a developer and occupy more lines of code.
The following methods from the Schema.DescribeSObjectResult can be called to verify whether the user has read, create, delete or update access on the object.
Enforce User Mode for Database Operations
Earlier when we had to fetch contact records using SOQL we used to run SOQL like this.
With Schema Describe checks this used to look like this
With User_Mode this can be refactored as below
As per Salesforce documentation, one should enforce FLS using WITH USER_MODE rather than WITH SECURITY_ENFORCED because of these advantages.As per Salesforce documentation, one should enforce FLS using WITH USER_MODE rather than WITH SECURITY_ENFORCED because of these advantages.
- User_mode considers polymorphic fields like Owner & Task.WhatId.
- User_mode processes all clauses in SOQL including where clause.
- User_mode finds all errors in SOQL query and one can use getInaccessibleFields() method on Query Exception to check for all errors.
Enforce User_Mode in DML statements as shown below:
Use AccessLevel class to enforce user_mode in database class methods.
Database.query method. See Dynamic SOQL.
Database DML methods
For example: –
When Database DML methods are run with AccessLevel.USER_MODE, you can access errors via SaveResult.getErrors().getFields(). With insert as user, you can use the DMLException method getFieldNames() to obtain the fields with FLS errors.
Enforce Security with stripInaccessbile method
This method is used to remove the fields and relationship fields from query and subquery results that user can’t access. It can also be used to remove inaccessible fields before DML operations. We can also use this method to sanitize sObjects that have been deserialized from untrusted sources.
Security and SObjectAccessDecision classes are used to access object and field level data protection. The access check is done on the basis of field-level permission of the current user in the context of the specified operation—create, read, update, or upsert.
** The ID field is never stripped by the stripInaccessible method to avoid issues when performing DML on the result.
This method checks source records for fields that don’t meet the FLS check for the current logged in user. It also checks for lookup and master-detail relationship fields to which current user doesn’t have access. Method returns list of sObjects without the fields that are inaccessible to the current user.
Use sObject.set() method to identify inaccessible fields.
For example, the return list contains Contact object, and the field email is inaccessible to the user. Because this field fails the field-level access check, the field isn’t set and isSet returns false.
This example code removes inaccessible fields from the query result. A display table for contact data must always show the email. The SSN__c must be shown only to users who have permission to read that field.
This code snippet removes inaccessible fields from the subquery. The user doesn’t have FLS on the phone field of contact object.
This example code removes inaccessible fields from sObjects before DML operations. The user who doesn’t have permission to create Rating for an Account can still create an Account. The method ensures that no Rating is set and doesn’t throw an exception.
Filter SOQL Queries Using WITH SECURITY_ENFORCED
WITH SECURITY_ENFORCED can be used to enable field and object level security permissions for SOQL SELECT queries in Apex including subqueries and cross object relationships.
WITH SECURITY_ENFORCED applies field- and object-level security checks only to fields and objects referenced in SELECT or FROM clauses It is not applied to clauses like WHERE or ORDER BY.
Insert the WITH SECURITY_ENFORCED clause:
- After the WHERE clause if one exists, else after the FROM clause.
- Before any ORDER BY, LIMIT, OFFSET, or aggregate function clauses.
If field access for either LastName or Description is hidden, this query throws an exception indicating insufficient permissions.
- One can’t use WITH SECURITY_ENFORCED in SOQL having polymorphic field relationships. For example, you cannot use WITH SECURITY_ENFORCED in the query below.
- TYPEOF with an ELSE clause is not supported in SOQL using WITH SECURITY_ENFORCED. TYPEOF is used in SELECT statement to specify the fields to be returned for a polymorphic relation.
A System.QueryException is thrown if any fields or objects in the SELECT clause using WITH SECURITY_ENFORCED are inaccessible to the user.
Apex Managed Sharing
Sharing is the process where permissions are granted to a user or a group of users to perform a few actions on records. Apex managed sharing is one of the ways to grant sharing along with Salesforce User Interface and Lightning platform.
Using Apex managed sharing developers can support sharing requirements programmatically using Apex or the SOAP API. Users with Modify All Data permission can create or update sharing on a record.
In Salesforce user interface, the Reason field on object specifies type of sharing used. In Apex this field is referred to as rowCause.
In order to provide sharing programmatically, we use share object associated with the object. For example, AccountShare is the sharing object name for Account, ContactShare is the sharing object for contact.
If custom object is CustomObjectA, then the share object is CustomObject__Share.
Share object consists of records of all 3 types, managed sharing, user managed sharing and Apex managed sharing. Sharing granted through OWD, Role Hierarchy, permissions like “view all” and “modify all” is not tracked using this object.
Every sharing object has the following fields.
Apex managed sharing used an Apex sharing reason. This helps developers to know why they shared a record with user or group.
Apex sharing reasons can be created from object’s detail page.
This is a code snippet for sharing job object.
Conclusion – Writing secure Apex code has never been more important. Developers are tasked with writing efficient and powerful code. To keep up security needs be shifted earlier in development life cycle. Using the above-mentioned practices, Salesforce developers can write secure code. This also ensures that the Apex code meets the latest security guidelines.