The following security best practices apply when developing Force.com Lightning Components or Applications.

Contents

General Lightning Security Considerations

Third-party Lightning components and apps operate in a special origin (lightning.force.com) that's shared with Salesforce-authored Lightning code -- in particular, setup.app, which controls many sensitive security settings. Visualforce applications, by contrast, are served from a different domain (force.com) that isn't shared with Salesforce code. Because Lightning code shares the same origin as Salesforce-authored code, increased restrictions are placed on third-party Lightning code. These restrictions are enforced by LockerService and a special Content Security Policy. There is also additional scrutiny in the AppExchange security review.

When developing Lightning apps, make sure to enable LockerService in your Developer Edition org. It is also highly recommended to enable strict CSP in order to protect your organization's security controls from vulnerabilities in third party lightning components that may be installed in your org.

Content Security Policy for Lightning Components

Lightning components are currently subject to a Content Security Policy (CSP) with the following directives. See CSP Directives for more information.

Directive Summary
default-src 'self' Default policy that resources may be only loaded from the same domain (lightning.force.com).
script-src 'self' Scripts may only be loaded from the same domain (no external script loads). Use static resources to store all scripts.
'unsafe-eval' eval() and related reflection operations are not blocked by the CSP but are blocked in the security review, as future CSP settings will not allow unsafe-eval. Do not write any code using eval() in order to prevent your components from malfunctioning when CSP is tightened.
'unsafe-inline' Inline JavaScript is not blocked by the CSP but is blocked in the security review as future CSP settings will not allow unsafe-inline. Do not write any code using inline JS in order to prevent your components from malfunctioning when CSP is tightened
object-src 'self' <object> <embed> and <applet> elements can only be loaded from the same domain (use static resources).
style-src 'self' CSS styles can only be loaded from the same domain (use static resources).
img-src 'self' Images can only be loaded from the same domain.
"img-src 'http:' 'https:' 'data:' Images can only be loaded via http, https, or data URIs. The security review requires https.
style-src 'https:' CSS styles can only be loaded via https.
media-src 'self' Audio and video elements can only be loaded from the same domain.
frame-ancestors https: The page can be embedded only via an https parent frame.
frame-src https: All frames must be loaded via https.
font-src https: data: Fonts can be loaded via https and data URIs.
connect-src 'self' XHR callbacks and websockets can only connect back to the same domain.

Additional Restrictions For JavaScript in Lightning Components

In addition to the Content Security Policy, the security review imposes the following requirements on JavaScript:

  • JavaScript within a Lightning component can only modify or read the DOM elements belonging to the same namespace. For example, it cannot modify document head or directly access the DOM element belonging to another namespace. To modify other components, use the public api of the component: aura:methods, aura:attributes, or events.
  • Events may only be fired within a controller or component file, but not in a renderer. Do not change the attributes of other components in a render, to avoid rendering loops.
  • For loading script or style resources, use the ltng:require component rather than including the resource via a script or link tag.
  • Do not overwrite native window or document functions.
  • Avoid inline JavaScript except when referencing JavaScript controller methods in the component markup. For example:
  <div onmouseover="myfunction" >foo</div> //bad
  <div onmouseover="{!c.myControllerFunction}" >foo</div> //OK
  • The code must be written to work with a CSP policy that bans eval as well as unsafe-inline, therefore avoid eval(), Function() as well as inline javascript.
  • When submitting a Lightning component or app for security review, include all source JavaScript files in static resources, as we cannot review minified code directly. Failure to do so will delay the review of your components until we get the appropriate source files. This also applies to sources that compile to Javascript.

Component Security Boundaries and Encapsulation

In Apex, every method that is annotated @AuraEnabled should be treated as a webservice interface. That is, the developer should assume that an attacker can call this method with any parameter, even if the developer's client-side code does not invoke the method or invokes it using only sanitized parameters. Therefore the parameters of an @AuraEnabled method should:

  • not be placed into a SOQL query unsanitized
  • not be trusted to specify which fields and objects a user can access

Whenever an @AuraEnabled method modifies sObjects, full CRUD/FLS as well as sharing checks should be made to ensure that the client does not elevate their privileges when invoking this method. These checks need to be performed on the server (in Apex). Note that this is different than the situation with VisualForce, in which CRUD/FLS checks can be performed for you by the visualforce presentation layer. This means porting code from VisualForce to Lightning requires the addition of CRUD/FLS checks each time an sObject is accessed.


Because Lightning components are meant to be re-usable and shareable, each global or public attribute should be viewed as untrusted from the point of view of the component's internal logic. In other words, don't take the contents of an attribute and render them directly to the DOM via innerHTML or $().html(). It does not matter whether, in your app, the attributes are provided by another component you control. When you need to perform a raw HTML write or set an href attribute, then the attribute must be marked sanitized in your javascript code.

Access Control in Apex Controllers and Supporting Classes

When Lightning components invoke server side controllers, the developer must ensure that the server-side read/write operations do not subvert the organization's security policy as set by the user's profile and sharing permissions. All access control enforcement must occur server-side, because the client is under the control of the attacker. Fortunately you can ensure that your server-side code is safe to use with Lightning components by taking some additional steps when writing your Apex Classes.

Sharing in Apex Classes

All controller classes must have the with sharing keyword. There are no exceptions. In some cases, your code will need to elevate privileges beyond those of the logged in user. For example, you may have a method that returns summary data computed over fields that the logged-in user cannot access. In this case, the controller must still use the with sharing keyword, but a specific aura-enabled method may call a helper method in a class that is explicitly marked without sharing. All privileged operations should be placed into these helper classes, and each privileged helper class should perform a single privileged function and no unprivileged functions. All other classes must be with sharing.

   public class ExpenseController() {  //Unsafe
       
       @AuraEnabled
       public static String getSummary() {
           doPrivilegedOp() //should not be here  
       }
   }


   public with sharing class ExpenseController() { //safe 
   
       @AuraEnabled
       public static String getSummary() {
           HelperClass.doPrivilegedOp()
        }
   }


   public without sharing HelperClass() { //safe, not a controller and limited functionality
   
       protected static String doPrivilegedOp() {
           //calculate roll-up field here
       }
   }


The lack of a sharing keyword is to be avoided, as it makes auditing difficult and creates permission ambiguities that depend on control flow. Mark all (non-interface) classes as either with sharing or without sharing. Never defer the sharing decision to runtime. All global classes, classes that expose webservices, or allow for remote invocation (such as Aura Enabled classes and remote action classes) must always use the with sharing keyword.

CRUD/FLS Enforcement

CRUD/FLS permissions are not automatically enforced in Lightning components or controllers, nor can you rely on Lightning components to enforce security (as the client is under the control of the attacker, so all security checks must always be performed server-side). You must explicitly check for for isAccessible(), isUpdateable(), isCreateable(), isDeletable() prior to performing these operations on sObjects. More information about CRUD/FLS enforcement.


   	public with sharing class ExpenseController {
   
         //ns = namespace, otherwise leave out ns__  
        @AuraEnabled
        public static List<ns__Expense__c> get_UNSAFE_Expenses() {   //vulnerable
            return [SELECT Id, Name, ns__Amount__c, ns__Client__c, ns__Date__c, 
                ns__Reimbursed__c, CreatedDate FROM ns__Expense__c];
         } 
   
   		@AuraEnabled
   		public static List<ns__Expense__c> getExpenses() { //safe
   	        String [] expenseAccessFields = new String [] {'Id',
   	                                                       'Name',
   	                                                       'ns__Amount__c',
   	                                                       'ns__Client__c',
   	                                                       'ns__Date__c',
   	                                                       'ns__Reimbursed__c',
   	                                                       'CreatedDate'
   	                                                       };
   
   
   	        // Obtaining the field name/token map for the Expense object
   	        Map<String,Schema.SObjectField> m = Schema.SObjectType.ns__Expense__c.fields.getMap();
   
   	        for (String fieldToCheck : expenseAccessFields) {
   
   	          // Check if the user has access to view field
   	          if (!m.get(fieldToCheck).getDescribe().isAccessible()) {
   
                   //also pass error to client
                   throw new System.NoAccessException()
   
                   //included to quiet editor
                   return null;
   	            }
   	         }
     
   	         //now it is safe to proceed with call
   	         return [SELECT Id, Name, ns__Amount__c, ns__Client__c, ns__Date__c, 
   	                     ns__Reimbursed__c, CreatedDate FROM ns__Expense__c];
        	       
   	       } 
   	 }

Field Validation Concerns

For purposes of threat modeling, the client is under the control of the attacker, therefore you cannot rely on client-side field validation to sanitize data written to the server. Client side field validation has an important usability role -- avoiding round trips for normal (non-malicious) users. Nevertheless, if you require field-level validation of your sObject data model, then this must be performed with triggers. Note that validation in the aura-enabled server-side controller is also insufficient as users can use the SOAP/REST API to modify objects directly, bypassing the Apex Controller as well as the UI.

Cross Site Request Forgery

In order to prevent CSRF attacks, do not invoke any server-side controller method that performs a DML operation automatically as the result of a page load. Specifically, do not invoke server-side DML controller method as onInit handlers, or afterRender handlers (if rendering is performed automatically on page load).

   ({
       doInit: function(cmp) {
           var action = cmp.get("c.updateField"); //vulnerable to CSRF
           [...]
           $A.enqueueAction(action);
          },
       handleClick: function(cmp, event) {
           var action = cmp.get("c.updateField"); //not vulnerable to CSRF
           [...]
           $A.enqueueAction(action);
          } 
   })

The key is that the DML operation not be performed without an event stemming from human interaction, such as a click. CSRF only applies to server-side DML operations, not operations that update client-side component attributes.

Cross Site Scripting

Component markup is rendered differently than standard VisualForce markup (which is rendered server-side) or javascript micro-templating frameworks (which are usually rendered with innerHTML). In Lightning, component markup must be valid xhtml, and the markup is parsed and rendered to the DOM with standard DOM accessors such as setAttribute and textContent. Therefore no html parsing occurs during component markup rendering, and there is no possibility of breaking out of attribute values or nodeValues. When attributes are interpolated into markup, they can only occur as attribute values or the text content of DOM nodes. Lightning attribute values cannot be inserted as attribute names, or portions of attribute values. This means certain constructions that would be valid in VisualForce or most micro-templating frameworks are invalid aura expressions, and most of the remaining constructions are automatically safe:

   <div>Here is a <b> {!v.myvalue} </b> bold value</div> <!-- always safe-->
   <div title="{!v.myvalue}">a div</div>   <!-- always safe-->
   <div title="{!v.myvalue}">a div</div>   <!-- always safe-->
   <div {!v.myvalue}>a div</div>   <!-- will not compile-->
   <div title="Here is a {!v.myvalue}">a div</div>   <!-- will not compile -->
   <div title="{!'Here is a ' + v.myvalue}">a div</div>   <!-- always safe-->

Because of this, no encoding is ever performed by the framework when rendering component markup, nor are any encoding functions provided by the framework.

However, there is still the possibility of using unsafe attributes:

   <a href="{!v.foo}">click</a> //unsafe: foo=javascript:..
   <iframe src="{!v.foo}"/> //unsafe: foo=javascript:..

The following is a partial list of unsafe combinations of tags and attributes that should be avoided, if possible within lightning component markup in order to avoid assigning lightning attributes values to unsafe attribute values:

Tag Attribute(s) Issue
(any) href or xlink:href javascript: or data: pseudo-schemes
any on* (event handler) js execution context
iframe, embed src javascript: pseudo scheme
iframe srcdoc html execution context
form formaction js execution context
object data js execution via data uri
animate to, from js execution context
any style css injection


This is only a partial list. See html5sec.org for more information about possible unsafe attributes. Of the above unsafe constructions, two are commonly used in component markup, namely anchor tags as well as style tags. There are several options for sanitizing these attributes.

Anchor tags can be sanitized when relative URLs are used:

   <a href="{!v.foo}">click</a> //unsafe: foo=javascript:..
   <a href="{!'/' + v.foo}">click</a> //forces scheme to be https or http

The same procedure can be used to control other URIs.

If you must handle both absolute and relative URIs, another option is to mark the attribute as private (for example if you are setting it in your code and are not setting it externally).

Alternately if you cannot mark the attribute as private and must set it in component markup (rather than a custom renderer) you will need to sanitize the attribute yourself with an onChange event:

component:

   <aura:component>
       <aura:attribute name="attr" type="String" />
       <aura:handler name="change" value="{!v.attr}" action="{!c.sanitizeUrl}" />
       <aura:handler name="init" value="this" action="{!c.sanitizeUrl}" />
       <a href="{!v.attr}">click here</a>
   </aura:component>

with controller:

   ({ 
       sanitizeUrl : function(cmp, event, helper) {
           var el = document.createElement('a'); 
           el.href = ('getParam' in event) ? event.getParam('value') : cmp.get('v.attr');    
           if (el.protocol !== 'https:') { 
               cmp.set('v.attr', ' ');
           }
   })

Note that the scheme is parsed by the browser rather than with string handling functions. Also note that both the init and change events must be filtered to make sure that only controlled schemes are rendered or re-rendered in your component's markup.

Because the binding between component markup and lightning attributes is automatic, unless you intercept value changes, your code cannot sanitize attributes set from outside your component. Moreover, it is a bad practice to rely on the code that instantiates or interacts with your component to pass you safe values. Relying on the caller to give you data that is safe creates a security dependency between the internal structure of your component and all of the possible callers. In large applications, this typically results in failure to sanitize some input.

Because the above sanitization technique is heavyweight, it is preferable to use only relative URLs in component markup, or to use only private attributes (for example, if the URL is pulled from a URL type on the server).

For style tags, CSS as a language is difficult to sanitize in such a way as to prevent style injection. Moreover, the 'type' fields within lightning attributes are not enforced -- e.g. field marked 'Boolean' may well contain strings, etc. Therefore it is a bad practice to pass attribute values to style tags. Instead use tokens, private attributes, or javascript to manage style changes.

Within javascript, the same cross site scripting issues are possible as with any other javascript code. Be aware that no encoding functions are provided, so if you must use html rendering functions, then place a third party encoding library such as secureFilters into your helper (loading this library via static resources creates some race conditions that add complexity to your code). Then, use the secureFilters.html function to sanitize external data passed to rendering functions or third party libraries that use rendering functions:

   rerender: function(cmp, event, helper) {
       var el = cmp.find("foo").getElementById("bar");
       var myattr = cmp.get("v.myattr");
       el.innerHTML = "<b>" + myattr +"</b>"; //unsafe
       el.innerHTML = "<b>" + cmp.helper.secureFilters.html(myattr) +"</b>"; //safe

...and place the secureFilters code into your helper. Alternately, use an html encoding function that performs the same substitution operations as secureFilters' html() function.

If in the future, encoding functions are provided natively by the framework, this section will be updated with new instructions. Do not roll your own encoding functions.

Do not rely on CSP to prevent XSS, as the presence of CSP policies will depend on how your component is surfaced and what the organization's policies are, which is subject to change at runtime. Additionally, CSP will not protect your code from html or style injection.

For more information about general XSS issues, see our Secure Coding Guidelines.

Arbitrary Redirect

When re-directing to a third-party side:

  1. Use HTTPS.
  2. Ensure that the domain is hard coded into the source or stored in a custom setting. Storing the domain in a custom object field is not considered sufficient protection.

Secret Inputs

In Visualforce, user password entry should be performed with <apex:inputSecret > tags. The Lightning equivalent for this is the <ui:inputSecret /> component.

Third Party Frameworks

Check out this blogpost about using third party frameworks within LockerService.