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:

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.

or 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

- HTMLENCODE:

<apex:outputText escape="false"> Hello

{!HTMLENCODE($CurrentPage.parameters.userName)}

</apex:outputText> - JSENCODE:

<script>var

userId='{!JSENCODE($CurrentPage.parameters.username)}';

</script>

- HTMLENCODE: - JSENCODE: Consider using the secure-filters

Consider using the 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.

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 using the standard Salesforce UI with no custom Apex code When a VisualForce page uses a standard controller

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 a VisualForce page references objects using standard notation, also known as “referencing objects directly”: 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.

When creating, reading, updating, or deleting a record with Lightning. In custom Apex controllers or extensions to standard controllers

In custom Apex controllers or extensions to standard controllers In custom triggers

In custom triggers In custom Apex web services

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}">

When referencing objects indirectly from custom Apex code:

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.





, , , and use the when creating, reading, updating, and deleting objects or fields. 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.





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





if it or 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). For more information on the differences between custom settings and custom metadata types, please see: Compare Custom Settings and Custom Metadata Types 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.

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];

SOQL query: 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.

requests for state-changing operations. The Salesforce platform automatically applies CSRF protections for 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)