Tuesday, January 10, 2017

JavaScript Security for Visualforce

I thought I'd touch on some of the security considerations that should be made when working with JavaScript from Visualforce.

Cross Site Scripting (XSS)

The risk of cross site scripting is always something to be aware of when developing web applications and needs to be considered when using JavaScript in Visualforce as well. Generally speaking, you want to prevent untrusted user input from being reflected back into JavaScript code.

As an example - say you were trying to read a page parameter info JavaScript with the following (Example only - DON'T DO THIS):

<apex:page>
    <script>var foo = '{!$CurrentPage.parameters.userparam}';</script>
</apex:page>

If you load this in the browser and include a &userParam=bar in the query string then the resulting HTML is:

Great! But what if someone puts something malicious into the URL? Something like 1';alert('All%20your%20Salesforce%20are%20belong%20to%20us');var%20foo='2. Here is the result in the browser:

And again from the page source:

So definitely not what we wanted. Notice how the apostrophe characters in the user input allow the code to take on an entirely different meaning. It would be very easy to extend the example to submit the current session cookies to an external resource - compromising you Session Id and pretty much everything else from there.

The solution here is to use JSENCODE to encode any text before reflection into JavaScript. This will use a backslash to escape any unsafe JavaScript characters, such as apostrophe (').

<apex:page>
    <script>var foo = '{!JSENCODE($CurrentPage.parameters.userparam)}';</script>
</apex:page>

See also:

Javascript Remoting with escape: false

When using Javascript Remoting beware the using {escape:false} in the configuration and loading the result into the DOM. Much like the basic XSS example above, it can be used to inject executing JavaScript into the page.

See also:

Avoid the urge to hack the Salesforce DOM

Not so long ago developers would put JavaScript in the sidebar so it would load with every page. This could then be used to manipulate the standard Salesforce DOM to do all sorts of things, such as showing/hiding components, adding additional validation, or changing their presentation.

Salesforce took umbrage with this approach as it opened up all sorts of consistency and security problems. As such, they shut the practice down in the Summer '15 release. Needless to say, those who had used the sidebar to manipulate the page found their workaround hack solution no longer working. Best to avoid these sorts of unsupported shenanigans if you can as they will be closed off sooner or later.

See also:

Protect the Session Id / AccessToken

The Session ID is the key to the kingdom. If someone can get hold of yours they can interact with Salesforce like you would*. If you can avoid exposing it in Visualforce then do so. E.g. Use JavaScript Remoting rather than interacting with the APIs directly.

Depending on the context where you request it, you can get a "second class session id" that doesn't grant you the same level of access as a full "first class" UI session type.

For instance, a Session ID in Visualforce from can be used to make API calls, but can't be used to access the full web UI (Such as via frontdoor.jsp).

Checking the User Session Information page can show the different Session Types that get created. In the example below note the TempVisualforceExchange Session that was created from the Parent UI Session.

See also:

* There are some exceptions on if they can use it if "Lock sessions to the IP address from which they originated" or "Enforce login IP ranges on every request" are enabled.

Static Resource rather than CDN

Using a CDN such as Google Hosted Libraries or the Microsoft Ajax Content Delivery Network to bring in something like jQuery is appealing, but can open you up to security problems and overall make the app exchange security review more troublesome than it needs to be. Instead consider using a zip file in a static resource and referencing the contents of the zip using URLFOR.

See also:


Further reading: