Escaping Interpolated Values in Hippo Templates
If untrusted values are not correctly escaped when included in web page markup, they can easily make the site susceptible to attacks. To reduce the risk of developer mistakes, many template engines can take care of escaping by default. FreeMarker template engine is no exception. Unfortunately, Hippo CMS default configuration doesn't enable automatic escaping in FreeMarker templates.
Because of that, developer mistakes can make the site vulnerable to Cross-site scripting (XSS) attacks as in the following example:
<button id="the-button" data-id="${id}">Click me!</button>
<script type="application/javascript">
const button = document.getElementById('the-button');
button.addEventListener('click', function(event) {
const id = event.target.getAttribute('data-id');
alert("Id value: " + id);
});
</script>
Let's imagine that the id
value comes from an untrusted source, e.g. a query parameter:
public class SampleComponent extends CommonComponent {
@Override
public void doBeforeRender(final HstRequest request, final HstResponse response) {
super.doBeforeRender(request, response);
request.setAttribute("id", request.getRequestContext().getServletRequest().getParameter("id"));
}
}
If the site visitor clicks on a carefully crafted URL provided by an attacker, injected JavaScript code could be executed on the page. For example, the query string value of ?id=5%22%20onclick%3D%22console.log(%27injected%27)
would result in the following HTML sent to the client:
<button id="the-button" data-id="5" onclick="console.log('injected')">Click me!</button>
Although Chrome would protect the user from such an attack, this isn't true for all browsers. In the latest version of Firefox (65.0.1 at the time of writing), the button would still work as expected, but injected
would be emitted to the console at the same time. Depending on the contents of the site, the attacker could perform a more malicious action, e.g. send some data from the site to his server.
The developer can prevent such attacks if he always escapes untrusted values before using them (e.g. by using the html
FreeMarker built-in):
<button id="the-button" data-id="${id?html}">Click me!</button>
There are several ways to configure automatic escaping of values in FreeMarker. Although Hippo CMS doesn't do that yet by default, it can still be done with certain restrictions.
Configuring Output Format Globally
HTML output format can be configured globally in the site web.xml
file by adding the following init-param
element to Freemarker's servlet configuration:
<init-param>
<param-name>output_format</param-name>
<param-value>HTMLOutputFormat</param-value>
</init-param>
This will enable auto-escaping. As a result, the html
built-in is not allowed to be used anymore. Any occurrences will result in an error:
Using ?html (legacy escaping) is not allowed when auto-escaping is on with a markup output format (HTML), to avoid double-escaping mistakes.
If you decide to go this route, you will need to remove all usages of this built-in. Not only from your templates, but also from default templates generated by the Essentials setup application. If that means to much work or is too risky for your current state of the project, you can still enable auto-escaping selectively.
File Extension Based Output Format
FreeMarker supports file extension based output formats which can be used to only enable auto-escaping in files with a specific file extensions (i.e. .ftlh
for HTML format instead of the default .ftl
). This approach allows you to only change the behavior in a subset of files with the different file extension.
To enable this behavior, the following init-param
element must be added to Freemarker's servlet configuration the site web.xml
file:
<init-param>
<param-name>recognize_standard_file_extensions</param-name>
<param-value>true</param-value>
</init-param>
Additionally, the corresponding servlet-mapping
element in the same file must be updated to include the new .ftlh
extension:
<servlet-mapping>
<servlet-name>freemarker</servlet-name>
<url-pattern>*.ftl</url-pattern>
<url-pattern>*.ftlh</url-pattern>
</servlet-mapping>
Using the Hippo CMS console, the extension must also be added to the webfiles
module configuration; its includedFiles
property in the /hippo:configuration/hippo:modules/webfiles/hippo:moduleconfig
node, to be exact.
However, even with all this set up, the files still don't refresh if they are changed while the site is running which makes testing of changes in templates very time consuming. Based on this issue comment, there might be other problems with this approach in Hippo CMS that aren't immediately obvious. Therefore, I don't recommend it.
Specifying Output Format in Templates
Output format can also be specified in each individual FreeMarker template, no matter its extension:
<#ftl output_format="HTML">
This will enable auto-escaping for that file. The only downside is that the developers can forget to add the header to a file and therewith potentially expose the page to attacks.
Excluding Values from Auto-Escaping
In any file with HTML output format specified (implicitly or explicitly), all values will be escaped. For most cases this is ok. But there might be some trusted values for which you know that they are already escaped. One such example is the action URL value for forms, as provided by the hst:actionURL
tag:
<@hst.actionURL var="actionLink"/>
If used in a template with auto-escaping enabled, the special characters in it will be escaped (all occurences of &
will become &
) and the POST request will not reach the doAction
method in the corresponding component. To avoid this, the no_esc
built-in must be used:
<form action="${actionLink?no_esc}" method="post">
<!-- form fields here -->
<button type="submit">Submit</button>
</form>
This will keep the URL unmodified and ensure that the POST request will be processed by the component as expected.