Start tracking your progress
Trailhead Home
Trailhead Home

Prevent XSS in Lightning Platform Applications

Learning Objectives

After completing this unit, you'll be able to:
  • List the three Visualforce encoding functions supplied by the platform.
  • Identify the context in which a merge field is rendered in your code.
  • Apply the contextually correct Visualforce encoding method to your code depending on location.

Platform Provided Encoding Functions

While the automatic HTML encoding provided by the platform is a fantastic default protection for a majority of use cases, it’s not a complete solution. As we saw in the previous unit, HTML encoding only protects your users in cases where user controlled input is being inserted in HTML.

So how do we deal with these other cases?

  • Code in script context (for example, code between <script> tags)
  • Code in style context (for example, code between <style> tags)
  • Code in script context inside HTML context (for example, JavaScript action handlers in HTML tags)

And what about when encoding is disabled by escape=false or escape:false in JavaScript remoting?

Luckily Salesforce provides encoding functions in both Visualforce and Apex. In this unit, we’ll cover each of these along with example code that you can walk through to learn how to remediate XSS vulnerabilities.

Platform Encoding in Visualforce

In Visualforce, the platform has three main encoding functions that developers can use to neutralize potential XSS threats: HTMLENCODE, JSENCODE, and JSINHTMLENCODE. To choose which encoding to use, consider how your browser is parsing the output.

  • If the value is going to be parsed by the JavaScript parser, use JSENCODE().
  • If the value is going to be parsed by the HTML parser, use HTMLENCODE().
  • If it’s a combination of both …

We’ll give examples to illustrate where and when to apply each encoding function.


JSENCODE() is a function that developers can use to perform JavaScript encoding of input prior to reflection in JavaScript context. This function encodes text and merge field values for use in JavaScript by inserting escape characters, such as a backslash (\), before unsafe JavaScript characters, such as the apostrophe ('). The function is used in cases where a merge field is directly used as a JavaScript variable.

Here’s some Visualforce that’s vulnerable to XSS.

    var x = '{!$CurrentPage.parameters.userInput}';

Consider an attacker who supplies a value for user input of

userInput'; alert(1); //

This causes the application to break out of the string, enabling the attacker’s code to execute this script.

    var x = 'userInput’; alert(1); //’;

To ensure that unsafe characters are properly encoded and aren’t executed by the JavaScript parser, we have to wrap the merge field with the JSENCODE function.

    var x = '{!JSENCODE($CurrentPage.parameters.userInput)}';

JSENCODE() in Action!

Now let’s learn how to fix a JavaScript-based XSS vulnerability using JSENCODE
  1. Log in to the Kingdom Management developer org and select the Cross-Site Scripting (XSS) app.
  2. Click the XSS Visualforce Mitigations Demo tab.
    You should see an image-based XSS injected into the page, defacing it.
  3. Click the Visualforce and Apex links at the bottom of the page to find the vulnerable code.


    public pageReference JSXSS(){
        title = 'THEME VIOLATION!!!!\';var newHTML = document.createElement(\'div\');newHTML.innerHTML = \'<img src="" />\';document.body.appendChild (newHTML);var x =\'x';
        return null;


        var vip = '{!title}';
    <apex:commandButton value="Click here to view the JavaScript-based XSS!" action="{!JSXSS}"/>

    You’ll notice that when the button is clicked on line 36 it called the JSXSS function in Apex. This function sets the title to an XSS payload which is then rendered in Visualforce on line 14.

    We’ll need to put some encoding around the output in line 14 to prevent this attack!

  4. Edit the Visualforce page by changing the following from
    var vip = '{!title}';


    var vip = ‘{!JSENCODE(title)}’;
  5. Click Save and navigate to the XSS Visualforce Mitigations Demo tab.
  6. Click the JavaScript-based XSS button again.

If no image appears on the screen, then you’ve successfully mitigated XSS!


HTMLENCODE() is a function that developers can use to perform additional HTML encoding of input prior to reflection in HTML context. For most use cases, developers don’t need to use this function, but it is required when the default platform encoding is turned off or when you’re adding user-controllable input directly to the DOM.

Consider the following example.

<apex:outputText escape="false" value="<i>Hello {!Account.Name}</i>" />

This Visualforce code is vulnerable to XSS because the developer has disabled the built-in platform HTML encoding by setting the attribute escape to false. Since this code is in the HTML context, the developer has to wrap the merge field with HTMLENCODE to ensure the value is rendered as text, not code.

<apex:outputText escape="false" value="<i>Hello {!HTMLENCODE(Account.Name)}</i>" />

Perfect! Let’s take a look at a more complicated example.

<div id="test"></div>
    document.querySelector('#test').innerHTML='Howdy ' + '{!Account.Name}';

Here the developer is directly modifying the DOM to insert a merge field in HTML context. Let’s think about how your browser parses this code. First, the merge-field (Account.Name) passes through the HTML parser when the page is loaded. However, because the merge-field is in a script tag, the HTML parser doesn’t perform character reference substitution and instead passes the contents of the script block to the JavaScript parser. The JavaScript code then calls innerHTML, which performs HTML parsing (and character reference substitution).

Therefore the parsing order is JavaScript then HTML.

What this means is that we actually have to do two rounds of encoding on this merge field to fully neutralize any potential XSS threat. First we need to JavaScript encode, then we need to HTML encode.

<div id="test"></div>
    document.querySelector('#test').innerHTML='Howdy ' + '{!JSENCODE(HTMLENCODE(Account.Name))}';

HTMLENCODE() in Action!

In the Kingdom Management app, you’ve started developing a profile page. Your eventual goal is to include a “friend finder” feature. Because users have control over the values in their profiles, you need to make sure you protect against XSS. Let’s go through it.
  1. Log in to the Kingdom Management developer org and select the Cross-Site Scripting (XSS) app.
  2. Click the XSS Visualforce Mitigations Demo tab.

    On the Visualforce page you see the various parts of the user profile, as well as buttons for testing cross-site scripting. These should save some time while testing your defenses!

  3. Click the first button labeled Click here to view HTML-based XSS.

    An XSS in the form of an embedded image appears on the page. This code is injected via the user parameter in the URL:


  4. Use the link at the bottom of the page to view the Visualforce code and you see the following.
    <apex:outputText value="Welcome, <b>{!$CurrentPage.Parameters.user}</b>!" escape="false"/>

    Because of the escape=”false” setting, user-controlled content is rendered directly on the page.

  5. Edit the code, and add your defensive encoding as follows.
    <apex:outputText value="Welcome, <b>{!HTMLENCODE($CurrentPage.Parameters.user)}</b>!" escape="false"/>
  6. Click Save and navigate to the XSS Visualforce Mitigations Demo tab.
  7. Click the HTML-based XSS button again.

The name looks strange, but there’s no theme violation. You prevented the XSS!


JSINHTMLENCODE is a legacy Visualforce function that was introduced when the platform didn’t always automatically HTML encode merge-fields. JSINHTMLENCODE is effectively a combination of HTMLENCODE(JSENCODE()), so before the introduction of auto-HTML encoding, developers called this function when including merge-fields in JavaScript event handlers within HTML (that is, onerror, onload). Now that the platform auto-HTML encodes, it’s sufficient to just call JSENCODE().

Consider the following example.

<div onclick="console.log('{!JSINHTMLENCODE(Account.Name)}')">Click me!</div>

Here, because the platform automatically performs HTML encoding of values included in action handlers (that is, onclick), by using JSINHTMLENCODE the developer inadvertently performed two rounds of HTML encoding. While this isn’t strictly vulnerable, it will most likely break the application. A more appropriate solution is.

<div onclick="console.log('{!JSENCODE(Account.Name)}')">Click me!</div>

The JSENCODE function encodes text and merge field values for use in JavaScript within HTML tags by inserting escape characters before unsafe JavaScript characters and replacing characters that are reserved in HTML with HTML entity equivalents. This is used for cases like merge fields in JavaScript event handlers, since the merge field is reflected in both HTML and JavaScript context.


Let’s try out the JSINHTMLENCODE function.
  1. In your Kingdom Management developer org, click the XSS Visualforce Mitigations Demo tab.
  2. Click the JavaScript + HTML-based XSS button to demo the vulnerability.

    You should see an image defacing the page.

  3. To find the vulnerable code, click the Visualforce and Apex links at the bottom of the page.


        var html = '<br/><br/><b>---------------------</b>';
        html += '<br/>Personnel Name: {!JSENCODE(name)}';
        html += '<br/>Favorite color: {!JSENCODE(color)}';
        html += '<br/>Favorite animal: {!JSENCODE(animal)}';
        html += '<br/><b>---------------------</b>';
        document.getElementById('{!$Component.output2}').innerHTML = html;                        
    <apex:commandButton value="Click here to view the JavaScript + HTML-based XSS!" action="{!JSINHTMLXSS}"/>


    public pageReference JSINHTMLXSS(){
        color = 'THEME VIOLATION!!!! <img src=""/>';
        return null;

    When the user clicks the commandButton, the application called the JSINHTMLXSS function in Apex. This function sets an XSS payload to the color variable that is then processed by Visualforce.

    However, this time we’re wrapping the color variable in JSENCODE back in the Visualforce page before rendering it to the user. So why is this still a vulnerability?

    JSENCODE is an encoder specific to JavaScript context, so it prevents any JavaScript-based XSS such as “blue’;alert(‘hi’);//”. However, in this application our color variable is not just used in JavaScript context; it’s also written directly to the DOM via the innerHTML call, rendering the data as HTML. Therefore, in order to fully protect this application against XSS, both forms of encoding are required: JavaScript and HTML. Luckily, there is a combo encoding method provided by the platform: JSINHTMLENCODE().

  4. Modify the Visualforce to use JSINHTMLENCODE() rather than JSENCODE().
  5. Click Save and navigate back to the XSS Visualforce Mitigations Demo tab.
  6. Click the JavaScript + HTML-based XSS button again, and this time the embedded image shouldn’t appear.

The important lesson to take away is that there is a form of encoding for every context, and you have to be aware of the context in order to encode properly. Using the wrong encoding method can be dangerous!

Platform Encoding in Apex

Up until now we’ve focused completely on preventing XSS by modifying your Visualforce pages. But what if you need to encode in Apex?

In all honesty, encoding within the controller is strongly discouraged. You should always encode as close to the rendering context as possible (for example, in Visualforce). Whenever encoding occurs within a controller, a dependency is created between the view and the controller, whereas the controller should be agnostic to how the data is rendered. Moreover, this pattern isn’t robust because the Visualforce page may want to render the same variable in several different contexts, but the controller can only encode in one.

Nevertheless, you may need to encode within the controller. For example, if you are generating dynamic HTML from within your controller. To do so, anything under the control of a user requires encoding to prevent special characters from being interpreted as code instead of text. Salesforce provides various Apex encoding functions through the Lightning Platform ESAPI, which exports global static methods that you can use in your package to perform security encoding. This package can be installed in any Salesforce org as an unmanaged package.

The Lightning Platform ESAPI in Action!

We installed the Lightning Platform ESAPI package in the Kingdom Management developer org, so let’s see how you can use it instead of Visualforce encoding to prevent XSS!
  1. Log in to the Kingdom Management Developer org and select the Cross-Site Scripting (XSS) app.
  2. Click the XSS Apex Mitigations Demo tab.

    On the Visualforce page, you see some profile functionality and an XSS button. If you inspect the Visualforce code, you see all the merge fields rendered with escape=”false”.

    Title: <apex:outputText value="{!title}" escape="false" /><br/>
    Name: <apex:outputText value="{!name}" escape="false" /><br/>
    Favorite Color: <apex:outputText value="{!color}" escape="false" /><br/>
    Favorite Animal: <apex:outputText value="{!animal}" escape="false" /><br/>

    You may remember from our discussion earlier that setting the escape attribute to false disables the built-in HTML encoding provided by the platform.

  3. View the apex controller by clicking the link at the bottom of the page to see why the developers have done this.

    Everything is wrapped in the <b> tag! To maintain the designed effect, the encoding will need to occur in Apex, focused specifically on the user controlled values.

  4. Edit the controller and for each item that is sent to the page inside HTML content, wrap it in the ESAPI.encoder().SFDC_HTMLENCODE() method, as in this example.
    title = '<b>' + ESAPI.encoder().SFDC_HTMLENCODE(person.Title__c) +'</b>';
  5. Click Save and return to the XSS Apex Mitigations Demo tab
  6. Click the View the HTML-based XSS button again

You should see that the encoding functions neutralized that attack payloads but kept the rest of the data bolded as desired. All functionality is maintained even with the addition of security!