Note that there are four encoding functions, not three. You forgot about URLENCODE
. Here's an article that would really help you to read: Unescaped Output and Formulas in Visualforce Pages.
If you play around with the demo page provided there, you may better understand why you wouldn't want to encode for both JS and HTML when you only need one or the other. If you're just pulling a page parameter into your script and want to build logic around it, encoding the data for html may break your logic or add extra unnecessary work for yourself. Same goes for markup, if you just need it in your markup and you encode it for JS, you'll get some wonky results.
The key passage is here:
Depending on the placement of the tag and usage of the data, both the characters needing escaping as well as their escaped counterparts may vary.
More extensively quoted (emphasis added):
There are several functions that you can use for escaping potentially insecure strings.
HTMLENCODE
Encodes text and merge field values for use in HTML by replacing characters that are reserved in HTML, such as the greater-than sign (>
), with HTML entity equivalents, such as >
.
JSENCODE
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 ('
).
JSINHTMLENCODE
Encodes text and merge field values for use in JavaScript inside HTML tags by replacing characters that are reserved in HTML with HTML entity equivalents and inserting escape characters before unsafe JavaScript characters. JSINHTMLENCODE(someValue)
is a convenience function that is equivalent to JSENCODE(HTMLENCODE((someValue))
. That is, JSINHTMLENCODE
first encodes someValue with HTMLENCODE
, and then encodes the result with JSENCODE
.
URLENCODE
Encodes text and merge field values for use in URLs by replacing characters that are illegal in URLs, such as blank spaces, with the code that represent those characters as defined in RFC 3986, Uniform Resource Identifier (URI): Generic Syntax. For example, blank spaces are replaced with %20
, and exclamation points are replaced with %21
.
To use HTMLENCODE
to secure the previous example, change the <apex:outputText>
to the following:
<apex:outputText value=" {!HTMLENCODE(myTextField)}" escape="false"/>
If a user enters <script>alert('xss')
and clicks Update It, the JavaScript is not be executed. Instead, the string is encoded and the page displays Value of myTextField is <script>alert('xss')
.
Depending on the placement of the tag and usage of the data, both the characters needing escaping as well as their escaped counterparts may vary. For instance, this statement, which copies a Visualforce request parameter into a JavaScript variable:
<script>var ret = "{!$CurrentPage.parameters.retURL}";</script>
requires that any double quote characters in the request parameter be escaped with the URL encoded equivalent of %22 instead of the HTML escaped ". Otherwise, the request:
http://example.com/demo/redirect.html?retURL=%22foo%22%3Balert('xss')%3B%2F%2F
results in:
`<script>var ret = "foo";alert('xss');//";</script>`
When the page loads the JavaScript executes, and the alert is displayed.
In this case, to prevent JavaScript from being executed, use the JSENCODE
function. For example
<script>var ret = "{!JSENCODE($CurrentPage.parameters.retURL)}";</script>