SFDC Secure Development Cheat Sheet

by Zach Julian, on Dec 11, 2019 12:01:07 PM

Build secure Salesforce web applications and pass the SFDC AppExchange security review.


As with any web application, it’s important to follow secure development practices when building an application on the Salesforce platform. This is especially important if you plan to release your application on the AppExchange, which requires a mandatory security review. While many similarities exist between secure development on Salesforce and a standard web application, there are also platform-specific vulnerabilities (and built-in security controls) to be aware of.

This guide is designed to help developers build secure Salesforce web applications, whether the goal is to pass the AppExchange review or to simply improve an application’s security posture. The seven sections below are organized by vulnerability class:

  1. XSS 
  2. CRUD/FLS 
  3. Insecure Sharing 
  4. Insecure Storage
  5. SOQL Injection
  6. Insecure External Communication
  7. CSRF

Each section provides a brief overview and two bulleted lists: one which explains how the vulnerability arises, the second detailing steps to remediate the vulnerability. By following these guidelines, your application will be more prepared to pass the Salesforce security review.

For more details on the security review process, check out the AppExchange Security Review Trailhead module.


CROSS-SITE SCRIPTING (XSS)

All standard VisualForce and Lightning components have built-in output encoding to prevent XSS from user-supplied data. XSS vulnerabilities typically arise when output is explicitly disabled (e.g. escape="false"). Avoid disabling the XSS protections offered by the platform, and if you must, use the HTMLENCODE or JSENCODE methods to manually apply output encoding. Also, keep in mind that several locations do not enforce automatic output encoding and should not include user-supplied input, as detailed below.

CAUSE OF XSS VULNERABILITIES

  • When output encoding is explicitly disabled:
    -   Apex

    <apex:outputText escape="false">Hello
    {!$CurrentPage.parameters.userName}</apex:outputText>

    -    Lightning

    <aura:unescapedHTML>

  • When user-supplied input is inserted into a <script> block:

<script>var userld =
'{!$CurrentPage.parameters.username}';</script>

  • When user-supplied input is inserted into a <style> block

<style>body{background-color:
{!$CurrentPage.parameters.bgColor};}</style>

  • When user-supplied input is inserted into a JavaScript event handler:

<img src="image.jpg"
onload="loadImage({!$CurrentPage.parameters.targetImage})">

  • When a user controls the target of <apex:includeScript>:

<apex:includeScript value="{!$CurrentPage.parameters.scriptSrc}"/>

  • When an included third-party script has its own XSS vulnerabilities, such as outdated versions of jQuery.

 REMEDIATION OF XSS VULNERABILITIES

  • Avoid using escape="false" in VisualForce tags such as <apex:outputText>.
    This will disable the built-in VisualForce XSS protection.
  • Similarly, do not use <aura:unescapeHTML> on Lightning. If you need to style output,
    <ui:outputRichText> is likely sufficient for your application's needs and will not render raw HTML.
  • Do not place user-supplied input directly into <script> or <style> blocks, since output encoding is not automatically applied.
  • Do not place user-supplied input directly into JavaScript event handlers.
  • Do not allow users to control <script src> 
  • If user-supplied input must be included in the places mentioned above, use the HTMLENCODE or JSENCODE functions to safely render the data.
    -  HTMLENCODE:

    <apex:outputText escape="false"> Hello
    {!HTMLENCODE($CurrentPage.parameters.userName)}
    </apex:outputText>

    -  JSENCODE:

    <script>var
    userId='{!JSENCODE($CurrentPage.parameters.username)}';
    </script>

  • Consider using the secure-filters package from Salesforce.
  • Include third-party scripts via the staticresources folder instead of calling them from a remote location. This will prevent third parties modifying the remote script to include malicious code.
  • Ensure that third-party scripts included via static resources are not vulnerable to XSS, such as outdated libraries.

 

References
Apex Developer Guide - Cross Site Scripting (XSS)
Secure Coding - Cross Site Scripting

Trailhead Units
Understand Cross-Site Scripting (XSS)
Discover Built-in XSS Protections in Lightning Platform
Prevent XSS in Lightning Platform Applications


CRUD/FLS

CRUD (Create, Read, Update, Delete) and FLS (Field Level Security) control user authorization and are important concepts on the SFDC platform. Salesforce data is represented as objects (e.g., Account, Case, and Lead. Object permissions for an organization are defined by the administrator account via profiles and permission sets. There are standard objects (built-in to the platform), and custom objects (which are created using Apex code). CRUD and FLS refer to different levels of authorization:

  • CRUD settings enforce authorization at the object level - Account object
  • FLS settings enforce authorization at the field level - Name, Phone Number, Website fields within the Account object

The SFDC platform can run in “user context” or “system context.”

User Context

When operating in user context, CRUD/FLS permissions are automatically enforced for the user’s current role. The platform executes in user context in these scenarios:

  • When using the standard Salesforce UI with no custom Apex code
  • When a VisualForce page uses a standard controller
  • When a VisualForce page references objects using standard notation, also known as “referencing objects directly”:

    <apex:pageBlockTable value="{!Account}" var="a">
    <apex:column headervalue="Phone">
    <apex:OutputText value="{!a.Phone}"/>

  • When an application makes a standard API call (such as via the SFDC SOAP or REST APIs)

System Context

The SFDC platform supports system context to allow more flexible development, and is equivalent to running code as admin. Because system context does not automatically enforce authorization, enforcement must be added manually. The platform executes code in system context:

  • When creating, reading, updating, or deleting a record with Lightning. Unlike VisualForce, Lightning does not support any built-in authorization.
  • In custom Apex controllers or extensions to standard controllers
  • In custom triggers
  • In custom Apex web services
  • When referencing objects indirectly from custom Apex code:
    • Custom Controller:

      public class productManager {
      public pagereference deleteProduct(){
      productId = ApexPages.currentPage().getParameters().get('id');
      Product__c delRecord = [SELECT ID FROM Product__c where id = :productId LIMIT 1];
      delete delRecord

    • VisualForce page:

      <apex:page controller="productManager">
      <apex:commandButton value="Delete" action="{!deleteProduct}">

CAUSE OF CRUD/FLS VULNERABILITIES

  • When custom code (Apex and VisualForce/Lightning) does not manually enforce CRUD/FLS authorization checks when in system context.

  • When the isCreateable(), isAccessible(), isUpdateable(), or isDeletable() methods are not used in Apex code when creating, reading, updating, or deleting data to enforce authorization.

  • When Lightning components do not use the Lightning Data Service when referencing data.

 REMEDIATION OF CRUD/FLS VULNERABILITIES

  • Ensure that custom controllers/extensions, triggers, Asynchronous Apex, and Apex web services use the isCreateable(), isAccessible(), isUpdateable(), or isDeletable() methods when creating, reading, updating, and deleting objects or fields.
    • CRUD methods can be called on an object or a field:
      • Object: 
        if(!Lead.sObjectType.getDescribe().isAccessible()){
      • Field:
        if(!Schema.sObjectType.Opportunity.fiels.ExpectedRevenue.isAccessible()){
        • When calling CRUD methods on a field, the object-level permissions are checked automatically.

  • If using Lightning, the Lightning Data Service can be used by the UI to create, read, update, or delete records with automatic CRUD/FLS enforcement and no Apex, code. This is similar to the automatic enforcement offered by VisualForce.

  • It is crucial to enforce CRUD/FLS in @AuraEnabled Apex methods, as any Lightning component can call them.


References
Enforcing CRUD and FLS

Trailhead Units
Learn How Authorization Works in Force.com Apps
Identify CRUD and FLS Violations in Visualforce and Apex
Prevent CRUD and FLS Violations


INSECURE SHARING

Sharing is similar to CRUD/FLS enforcement in that it controls authorization rights to a record. Whereas CRUD/FLS controls determine a user’s ability to access a specific object or field, sharing controls relate to a user’s ability to view, edit, or delete records belonging to other users. Imagine a class that includes a SOQL SELECT statement for several fields of an object:

String query = 'SELECT ClientName, Revenue from ConsultingAccounts__c';

If this class was declared without the with sharing keyword, or with an explicit without sharing, this query would return the records belonging to all users, not just the current user, even if the sharing settings for the object were configured as Private. Because Apex executes in system context, sharing enforcement must be explicitly declared to prevent users from accessing or modifying data that does not belong to them.

CAUSE OF INSECURE SHARING VULNERABILITIES

  • When custom controllers, triggers, asynchronous Apex, or web services are not declared with sharing.

 

 REMEDIATION OF INSECURE SHARING VULNERABILITIES

  • Declare all controllers, triggers, asynchronous Apex, and web services with sharing.

  • Sharing settings are applied to methods by the class they are defined in, not the class they are called from.

  • Sharing is not inherited from an outer to inner class. Declare inner classes with sharing as well.

  • Child classes inherit with sharing if it extends or implements a parent class.

  • If there is a need to update a record that the user does not have access to based on user input, use without sharing to explicitly disable sharing enforcement for a class. The ‘Without Sharing’ section of this Stack Exchange post gives a good use case. Use without sharing carefully to ensure that user data is not unintentionally exposed. Only include code in a class declared without sharing if it explicitly requires it.


References
Using the with sharing, without sharing, and inherited sharing Keywords

Trailhead Units
Learn How Authorization Works in Force.com Apps
Identify and Prevent Sharing Violations


INSECURE STORAGE

It is common for Salesforce applications to store sensitive information including PII, credit cards, API keys, encryption keys, and passwords. Fortunately, the SFDC platform supports several methods for protecting sensitive data. The best option depends on the type of data being protected.

CAUSE OF INSECURE STORAGE VULNERABILITIES

  • When an application insecurely stores or transmits sensitive information.

 

 REMEDIATION OF INSECURE STORAGE VULNERABILITIES

  • When storing sensitive data, ensure that it is protected with one of the following methods:
    • Apex Crypto class. Most applicable for sensitive user-supplied data such as passwords, credit card numbers or PII.
      • Never hardcode encryption keys in Apex source code. Store encryption keys in a protected custom setting or protected custom metadata type.
    • Managed protected custom metadata types. In a managed package, this is most applicable for storing secrets which must be used by all users of the package, such as an API key used by the application. Cannot be read by subscribing organizations via Apex or the API when the visibility setting is set to: Protected (Managed).
    • Managed protected custom settings. Similar to protected custom metadata types. In a managed package, they cannot be read by subscribing organizations via Apex or the API when the visibility setting is set to Protected (Managed).
    • Named credentials. Named credentials are created by defining an external URL and credentials, so they are most applicable when storing authentication data for external services. Defined in Setup > Named Credentials. Please note users with the Customize Applications permission can view this data.
    • Encrypted custom fields. Most applicable for sensitive user-supplied data such as credit card numbers. Provides a 128-bit AES encrypted field which is only readable by users with the View Encrypted Data permission.
  • Ensure that sensitive information is not written to application debug logs.
  • Ensure that sensitive information is not transmitted via URL. Salesforce requires all sensitive information to be transmitted with POST requests.
  • Ensure that Salesforce credentials (usernames, passwords, or session IDs) are not stored externally when integrating with an external application. Use the OAuth flow to authenticate to an external application instead.
  • Do not hardcode secrets.


References
Storing Sensitive Data
Apex Developer Guide - Crypto Class 

Trailhead Module
Secure Secret Storage


INSECURE EXTERNAL COMMUNICATION

Salesforce applications often integrate with third-party resources, either as part of a composite application or by simply including embedded resources or links from outside of Salesforce in a native application. This section details several best practices about integration with external resources.

CAUSE OF EXTERNAL COMMUNICATION VULNERABILITIES

  • When the application insecurely integrates with external components.

 

 REMEDIATION OF EXTERNAL COMMUNICATION VULNERABILITIES

  • When using third-party libraries such as jQuery or other JavaScript, use the Static Resource functionality instead of embedding them from an external source. Although this requires you to manually update the static resource to new releases, it prevents the introduction of malicious JavaScript to your application if the third-party resource is compromised.
  • Use HTTPS over TLS 1.2 or above when communicating with or linking to third-party resources.
  • Do not include mixed content (i.e., do not mix HTTP and HTTPS resources in your application) Ensure that everything is transmitted over HTTPS.
  • Ensure that Salesforce credentials (e.g., usernames, passwords, or session IDs) are not stored externally when integrating with an external application. Use the OAuth flow to authenticate to an external application instead.
  • Do not transmit passwords via a GET request, as this results in insecure storage via logs, browser history, and referrer headers.


References
Static Resources

Trailhead Unit
Use Static Resources
Prevent Insecure Remote Resource Interaction


SOQL INJECTION

Like regular SQL injection, SOQL (Salesforce Object Query Language) injection can be exploited to exfiltrate data from a database. The root cause is also the same as SQL injection - inserting user-controlled data directly into a SOQL query. In general, the best solution is also the same - binding user input to a variable and including that in a static query.

CAUSE OF SOQL INJECTION VULNERABILITIES

  • The issue arises when user-supplied input is included directly in a SOQL statement without assignment to a bind variable, validation, or sanitization, such as:

    String customer = ApexPages.currentPage().getParameters().get('customer');
    string query ='select name, revenue from Customer__c where name = '+customer+' ORDER BY name DESC;'

 

 REMEDIATION OF SOQL INJECTION VULNERABILITIES

  • Assign user input to a bind variable and then include that in a static SOQL query:

    String customer = ApexPages.currentPage().getParameters().get('customer');
    Result = [select name, revenue from Customer__c where name = :customer order by name desc];

  • Typecast all integer or Boolean user-supplied input to their proper type:

    Integer limit = ApexPages.currentPage().getParameters().get('limit');
    string query = 'select name, revenue from Customer__c limit‘+string.valueOf(limit)+‘;'

  • If possible, create a white list of acceptable user input and reject all others:

    Integer field = ApexPages.currentPage().getParameters().get(‘field‘);
    if (field=='name'||field=='revenue'||field=='address'){
    string query ='select'+field+'from Customer__c limit 10;'
    else {
    //reject

References
Apex Developer Guide - SOQL Injection

Trailhead Units
Understand SOQL Injection
Prevent SOQL Injection in Your Code


CROSS SITE REQUEST FORGERY

The SFDC platform automatically includes anti-CSRF tokens in forms created with VisualForce or Lightning (e.g., via <apex:form> or <apex:commandLink>). The only way to introduce this vulnerability is to perform a state-changing operation via GET request, which occurs via the <apex:page action> handler. By not doing this, the entire class of vulnerability can be avoided.

CAUSE OF CSRF VULNERABILITIES

  • The issue arises when developers bypass the built-in CSRF defenses by making a state-changing operation via GET request.

 

 REMEDIATION OF CSRF VULNERABILITIES

  • Do not use GET requests for state-changing operations. The Salesforce platform automatically applies CSRF protections for POST requests.
  • Do not perform any state-changing operations via the <apex:page action> handler.


References
Apex Developer Guide - Cross-Site Request Forgery (CSRF)

Trailhead Unit
Prevent Cross-Site Request Forgery (CSRF)

Subscribe by Email

Comments