/may 9, 2023

An Introduction to Secure Coding with Template Engines

By John Simpson

Back in 2022 while browsing through lists of recently disclosed vulnerabilities, I happened upon some Adobe Commerce/Magento Open Source vulnerabilities [1], that were reported to be exploited in the wild and can be exploited to achieve remote code execution, a combination which always motivates me to take a quick look at the vulnerability. Adobe provided a simple patch file that effectively removes {{ and }} characters when encountered in input provided to two specific components and it is reasonable to assume that the vulnerability involves Magento's built-in templating system.

Although Magento uses its own custom templating system, these vulnerabilities got me thinking about the general challenges developers face when trying to ensure they are using a template engine in a way that does not introduce security flaws. After looking through mountains of documentation and even diving into the code of some of the most popular template engines, it became very clear that the amount of work required to use templates securely varies drastically depending on the specific engine and use-case.

This series of blog posts is borne out of that research with the goal to provide developers with simple, concise direction on using a template engine securely for several languages with each post being dedicated to a given language such as the following:

  • Java
  • .NET
  • JavaScript
  • Python

However, to kick off the series, I'd like to cover some of the basics of template engines, related vulnerabilities, and commonly seen mitigations.

Just in case you don't want all the details, you can jump to the tl;dr

Use cases for template engines

Broadly speaking, template engines are used for two purposes: Simple mixing of static content with dynamic data, and facilitation of user customization by allowing a user to harness the power and simplicity of templates.

Mixing static content and dynamic data

By far the most common usage involves simply mixing static content, such as the overall design of a shopping cart or product item page, with dynamic content, like the contents of a user's cart or the specific details of an individual product. This generally involves creating one or more templates using a language such as HTML with engine-specific directives that tell the template engine how to insert dynamic content.

As a simple example, an application that simply says hello to the current user might have a template like this:

<p>Hello {{user}}</p>

When the user requests the page, the server uses the template engine to "render" the template which replaces {{user}} with the username as determined by the application code and sends the generated page to the user.

Accepting user controlled templates

A far less common use case for a templating engine is to facilitate user customization by providing the ability to define all or a portion of a template directly. For example, this could be used as an appealing "low code" customization method for users of a SaaS or PaaS product that allows users to create and run their own storefront.

Expanding on the example above, a hypothetical Java application (using a pretend template engine) that aims to provide template-based customization of the greeting might look something like this:

String customGreeting = request.getParameter("customGreeting");

String templateBase = "<p>Hello " + customGreeting + "</p>";

String renderedTemplate = templateEngine.render(templateBase, context)

A user performing the customization can then provide convenient template directives, maybe something like {{username}}, {{firstName}}, or a combination of several directives, in order to have a consistent and customized greeting for each end user.

Although both use cases certainly require consideration of potential security implications, allowing users to directly supply any portion of a template directly to the template engine should be considered risky and as such requires an extra level of due diligence when selecting the template engine to use. This is because many template engines provide features that go well beyond simple substitutions found in the above examples. They can provide access to features of the underlying programming language such as loops, conditions, and even things like method/function calls and lambda expressions.

It is also important to note that not all template engines are used on the server side. There are also client-side templating engines used for applications that rely on a lot of client-side code and interact with the server primarily through an API. However, the primary focus of this series will be on server-rendered templates.

Template-related vulnerabilities

A security-minded developer might immediately notice a potential issue with templates. What happens when a template relies on data that a user controls? If the user plays nice, the page renders properly and everyone is happy. However, we all know that we can't simply trust user input and need to be aware of potential issues from malformed or outright malicious input that might wind up being fed into the template engine.

Without going into great detail about every type of vulnerability that may show up when using templates, we can instead think about the vulnerabilities in terms of whether the most immediate impact takes place on the client or server. To clarify further, ask yourself if the vulnerability initially allows an attacker to compromise confidentiality, integrity, or availability on the client or the server.

Client-side impact

On the client side of things, for the most part vulnerabilities are going to be caused by improper sanitization of user-controlled data when rendering the template.

A perfect example of this is cross-site scripting (XSS)[2]. Take our first example above and imagine that the value of user has been set to <script>alert('xss')</script>, a block of JavaScript that simply creates an alert popup with the text "xss". This would result in the rendered output of the template looking like this:

<p>Hello <script>alert('xss')</script></p>

When the rendered output is then sent to the user, the web browser, in this case the client, executes the JavaScript.

Server-side impact

As far as server-side impact goes, there is a larger variety of things that can go awry. Depending on the features supported by the template engine in use, a vulnerable application may allow an attacker to do things such as disclose information they should not have access to and in the worst case allow an attacker to execute arbitrary code on the server.

These vulnerabilities may be due to unknowingly using an unsafe feature in a template that renders user-controlled data or failing to configure the template engine itself properly. However, one of the most common causes is when a user is able to control a portion of the template itself, intentionally or not. This is known as a Server-Side Template Injection (SSTI) vulnerability, a concept originally presented by James Kettle at Black Hat 2015 [3].

Let's take a second look at the hypothetical Java example used previously. Pretend that the template engine in use provides the ability to execute an external program and render the output into the template using an exec directive. What if a user sets customGreeting to something like {{exec('cat /etc/passwd')}}? Here's what that effectively does to the code:

String templateBase = "<p>Hello " + "{{exec('cat /etc/passwd')}}" + "</p>";

String renderedTemplate = templateEngine.render(templateBase, context)

Now when the render() method is called, the template engine will execute cat /etc/passwd and place the result in the output which is then sent back to the user. Not good!

Vulnerability prevention

Prevention of the above vulnerabilities is highly dependent on the specific template engine in use but in general there are a few common practices found in most popular template engines.

Escape/encode dynamic content

This is primarily to prevent client impact vulnerabilities like cross-site scripting.

When configured properly many template engines perform this automatically, but as an example, the Apache FreeMarker template engine for Java used to require the use of an escape directive[4] like this:

<#escape x as x?html>**

  First name: ${firstName}

  Last name: ${lastName}

</#escape>**

This ensures that any HTML characters present in firstName or lastName are encoded to prevent arbitrary HTML tags, such as <script> from being injected into the rendered page.

Restrict access to potentially dangerous API calls

This is sometimes referred to as a "sandbox" and should be considered a mandatory feature to evaluate if your application intentionally allows users to control the contents of templates directly.

Even if you don't intentionally allow user controlled templates there may be instances where template directives are processed like in the case of a Spring View manipulation vulnerability[5] as detailed by Veracode researchers Michael Stepankin and Giuseppe Trovato. For this reason, it is generally a good idea to select a template engine that provides a sandbox-like feature regardless of your use case.

The Thymeleaf template engine, also for Java, has implemented its own version of a sandbox known as "Restricted mode" which cannot be disabled, nor is it easily configurable. However, it is only effective in certain circumstances and not intended to provide any security guarantees if a user directly controls the contents of a template.

This highlights the importance of understanding each template engine's sandbox mechanism and whether it will be sufficient for your use case, and is something we will cover in detail throughout the blog series.

Allow loading of templates only from specific locations

This mitigation is primarily intended to prevent the inclusion of potentially sensitive data in the rendered output of a template.

Many template engines provide an "include" mechanism that allows templates to be created in a modular fashion, effectively putting the contents of the included template into the main template before processing the rest of the template. However, without any restrictions on what type of files may be included, or where they may be included from, this could be used to include the contents of files with potentially sensitive data.

A simple example of this source restriction can be found in Apache FreeMarker where the FileTemplateLoader class, used for specifying a directory as a template source, accepts a baseDir parameter which restricts loading of any templates to files within that directory. This loader is automatically configured with the base directory when using the setDirectoryForTemplateLoading() method[6] of the Configuration class:

Configuration cfg = new Configuration(Configuration.VERSION_2_3_29);

cfg.setDirectoryForTemplateLoading(new File("/opt/application/resources/templates"));

The above configuration means that any attempt to include files outside of /opt/application/resources/templates will fail.

Final thoughts

Although most modern template engines provide some version of the above mitigations, the level of effort required to use them can vary greatly. On the one side, there are engines where some of these mitigations are simply built into the engine and may not be disabled. On the other end of the spectrum some mitigations may require wading through large amounts of documentation to figure out detailed configuration requirements, or "manual" activation of a mitigation on a case-by-case basis.

It is also very important to be aware of the fact that vulnerabilities in the template engines themselves can potentially cause even properly configured mitigations to be ineffective. As with the use of any 3rd-party library, developers must ensure that the version of the template engine used in their applications is inspected on a regular basis to see if any newly discovered vulnerabilities have been fixed, requiring an update to the libraries. This process can be made easier through the use of Software Composition Analysis (SCA) tools to regularly check your applications for vulnerable, out-of-date dependencies.

tl;dr

  • Template engines let developers render static content with dynamic data
  • Allowing users to specify template content requires extra caution and due diligence
  • Vulnerabilities from using template engines can have client-side or server-side impact
  • Many template engines offer some sort of mitigation or prevention for the most common vulnerabilities
  • The complexity of applying the preventative measures varies greatly across different template engines
  • Template engines themselves can contain vulnerabilities and must be kept up to date

In closing, I hope you have found this information useful and stay tuned for the next post in this series where we will start to dive in to the language-specific details of template engines.

References:

  1. https://helpx.adobe.com/ca/security/products/magento/apsb22-12.html ↩︎
  2. https://www.veracode.com/security/xss ↩︎
  3. https://portswigger.net/research/server-side-template-injection ↩︎
  4. https://freemarker.apache.org/docs/ref_directive_escape.html ↩︎
  5. https://www.veracode.com/blog/secure-development/spring-view-manipulation-vulnerability ↩︎
  6. https://freemarker.apache.org/docs/pgui_quickstart_createconfiguration.html ↩︎

Related Posts

By John Simpson

John Simpson is a Senior Security Researcher at Veracode. He is an experienced N-day vulnerability researcher and has performed root-cause analysis on hundreds of vulnerabilities over the span of several years. He now focuses on applying the knowledge gained from vulnerability research into improving Veracode’s static analysis service across a variety of languages and technologies.