Newer Version Available

This content describes an older version of this product. View Latest

Securing Data in Apex Controllers

Apex generally runs in system context, which means that the current user’s permissions, sharing rules, and field-level security aren’t taken into account by default during code execution. Because these security layers aren’t automatically enforced, developers who use Apex must take care that they don’t inadvertently expose sensitive data that would normally be hidden from users.

To work with Salesforce records, we recommend using Lightning Data Service, which handles sharing rules, CRUD, and field-level security for you.

Note

Sharing Rules

You can choose whether an Apex class enforces sharing rules by using the with sharing or without sharing keywords.

An @AuraEnabled Apex class that doesn’t explicitly set with sharing or without sharing uses a default or implicit value of with sharing.

However, Apex classes that don’t explicitly set with sharing or without sharing inherit the value from the context in which they are run. So when a class without explicit sharing behavior is called by a class that sets one of the keywords, it operates with the sharing behavior of the calling class. It’s a best practice to always use with sharing in Apex controllers used by Aura components.

Enforcing sharing rules by using the with sharing keyword doesn’t enforce the user’s permissions and field-level security. You must manually enforce CRUD permissions and field-level security separately in your Apex classes.

Object and Field Permissions (CRUD and FLS)

Enforce object-level and field-level permissions in your code by explicitly calling the sObject describe result methods (of Schema.DescribeSObjectResult) and the field describe result methods (of Schema.DescribeFieldResult) that check the current user’s access permission levels. In this way, you can verify if the current user has the necessary permissions, and only if he or she has sufficient permissions, you can then perform a specific DML operation or a query.

For example, 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, respectively. Similarly, Schema.DescribeFieldResult exposes access control methods that you can call to check the current user’s read, create, or update access for a field.

Example

This example shows the recommended way to query fields on a custom expense object.

1public with sharing class ExpenseController {
2
3    // ns refers to namespace; leave out ns__ if not needed
4    // This method is vulnerable. 
5    @AuraEnabled
6    public static List<ns__Expense__c> get_UNSAFE_Expenses() {
7        return [SELECT Id, Name, ns__Amount__c, ns__Client__c, ns__Date__c, 
8            ns__Reimbursed__c, CreatedDate FROM ns__Expense__c];
9     } 
10
11    // This method is recommended.
12    @AuraEnabled
13    public static List<ns__Expense__c> getExpenses() {
14        String [] expenseAccessFields = new String [] {'Id',
15                                                       'Name',
16                                                       'ns__Amount__c',
17                                                       'ns__Client__c',
18                                                       'ns__Date__c',
19                                                       'ns__Reimbursed__c',
20                                                       'CreatedDate'
21                                                       };
22
23
24    // Obtain the field name/token map for the Expense object
25    Map<String,Schema.SObjectField> m = Schema.SObjectType.ns__Expense__c.fields.getMap();
26
27    for (String fieldToCheck : expenseAccessFields) {
28
29        // Check if the user has access to view field
30        if (!m.get(fieldToCheck).getDescribe().isAccessible()) {
31
32            // Pass error to client
33            throw new System.NoAccessException();
34        }
35    }
36 
37    // Query the object safely
38    return [SELECT Id, Name, ns__Amount__c, ns__Client__c, ns__Date__c, 
39            ns__Reimbursed__c, CreatedDate FROM ns__Expense__c];       
40    } 
41}