Contents

Testing Object and Field Level Security Enforcement



What Is It?

Object (CRUD) and field (FLS) level security are configured on profiles and can be used to restrict access on object types and individual fields. Force.com developers should design their applications to enforce the organization's CRUD and FLS settings, and to gracefully degrade if a user's access has been restricted.


Sample Vulnerability

In most cases, VisualForce will automatically enforce CRUD and FLS when direct references to SObjects and SObject fields are used. If objects or field values are referenced as generic data types or data is copied to other elements, developers will need to implement the appropriate access control checks. For example, the below code passes an SObject field to a VisualForce page as a generic string and bypasses VisualForce's automatic CRUD/FLS enforcement:


<apex:page standardController="Account" extensions="MyAccountExtension">
  <apex:outputText value="{!name}" />
</apex:page>
public with sharing class MyAccountExtension {
  private Account a;
  
  public MyAccountExtension(ApexPages.StandardController ctr) {
    a = [SELECT Name FROM Account WHERE Id=: ctr.getRecord().Id]; 
  }
  public String getName() {
    return a.Name;
  }
}

Other potential areas where an organization's CRUD and FLS settings can be violated are:

  • Passing custom Apex classes that copy or wrap SObject data to VisualForce pages
  • All Apex web services
  • Lightning components don’t automatically enforce CRUD and FLS when you reference objects or retrieve the objects from an Apex controller, CRUD and FLS should be enforced when using the “@AuraEnabled” notation. (https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/apex_crud_fls.htm)
  • SObject updates, creates, or deletes done within Apex controllers or extensions


Please see the CRUD/FLS Enforcement Guide for more information.

Is My Application Vulnerable?

CRUD and FLS always needs to be enforced for create, read, update, and delete operations on standard objects. In the vast majority of cases, CRUD and FLS should also be enforced on custom objects and fields. Any application performing creates/updates/deletes in Apex code, passing data types other than SObjects to VisualForce pages, using Apex web services or the @AuraEnabled” notation should be checked that it is calling the appropriate access control functions.


How Can I Test My Application?

There two main areas to look at when testing applications: data displayed to the user and data modification.


Data Displayed to the User


Examine each VisualForce page and at the areas on the page where data is embedded using merge fields (i.e. {!object.field}). Merge fields directly referencing SObjects and fields will be automatically checked for CRUD/FLS by VisualForce during page rendering. Merge fields referencing SObject data through other objects like strings, integers, or Apex classes require that the page controller or controller extension perform the appropriate access control check. Generally this will be the isAccessible() method of the source SObject field's describe result.

Apex web services do not have a VisualForce layer to automatically enforce CRUD/FLS and always need to call isAccessible() on all SObject fields before returning data to the user, same goes for Lightning components or controllers.


Create, Update, and Delete Operations


Examine each Apex class that calls insert, update, upsert, delete, or similar commands. For create and update operations, each field assigned a value directly in Apex should have its describe result isCreateable() or isUpdateable() method checked before performing the operation. Fields that are assigned values from a VisualForce page using the apex:inputField tag are automatically checked for the appropriate CRUD/FLS access. If a user does not have the correct access, VisualForce will render apex:inputField elements as read-only.

Delete operations occur at an object level by nature and the object's describe result isDeleteable() method should be called instead of field-level checks. Controller extensions can also call the standard controller's delete() method to delete the active record, which will automatically check the user's CRUD access before performing the operation.

Apex web services and aura enabled methods always need to perform the appropriate access control checks on all objects and fields before performing create, update, and delete operations.

How Do I Protect My Application?

The easiest way to enforce CRUD/FLS is to perform operations in VisualForce and to operate directly on SObjects and fields. In this mode, VisualForce will automatically enforce an organization's CRUD and FLS settings. For applications where this is not possible, Apex provides a number of methods on the object or field describe result (i.e. the object returned from the getDescribe() call). A basic example is where the access checks are performed directly in the controller:

<apex:page standardcontroller="Contact" extensions="ContactUpdateExtension">
  <apex:pageMessages />
  <apex:pageBlock title="Contacts"> 
    <apex:outputField value="{!Contact.Name}" />
    <apex:outputField value="{!Contact.Phone}" />
  
    <apex:form>
      <apex:selectList value="{!statusToSet}">
        <apex:selectOption itemValue="Verified" itemLabel="Verified" />
        <apex:selectOption itemValue="Not Verified" itemLabel="Not Verified" />
        <apex:selectOption itemValue="Unknown" itemLabel="Unknown" />
      </apex:selectList>
      <apex:commandButton action="{!updateStatus}" value="Update" />
    </apex:form>
  </apex:pageBlock>
</apex:page>
public with sharing class ContactUpdateExtension {
  public String statusToSet {get;set;}
  private Contact c;
  
  public ContactUpdateExtension(ApexPages.StandardController ctr) {
    c = [SELECT Status__c FROM Contact WHERE Id=: ctr.getRecord().Id]; 
  }
  public PageReference updateStatus() {
    // Check if the user has update access on the Contact.Status__c field
    if (!Schema.sObjectType.Contact.fields.Status__c.isUpdateable()){
      ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.FATAL,
                                                 'Insufficient access to update status'));
      return null;
    }
    c.Status__c = statusToSet;
    update c;
    return null;
  }
}


Larger applications usually benefit from the creation of centralized classes that provide abstractions and bundling of these functions based on the type of operations and functionality offered by the application. Salesforce provides an open source library, the force.com ESAPI, to aid in performing these checks. Salesforce.com recommends that applications never assume a user will have access to all object fields or operations. Application should be designed to degrade gracefully and should provide administrators the ability to designate the desired application behavior if operations can not be completed in their entirety.