Server-Side Template Injection Introduction & Example

This article introduces Server Side Templates and explains why and how they can be susceptible to Server-Side Template Injection vulnerabilities. It includes examples of HTML, PHP and CSS code and concludes with a list of recommendations on how to protect your web applications from attacks that exploit SSTI vulnerabilities.

There are few topics that developers universally agree on. One example that often leads to heated discussions is the choice of the right source code editor. You may be a Vim fanatic or maybe you prefer the simplicity of Nano or the extensibility of Visual Studio Code. Meanwhile, others argue over whether Vim can be considered an editor at all. Sometimes it’s dismissed as a shortcut memory game, where there are – by design – no winners. But, I digress.

What’s actually important is that while there are many controversial topics in the programming profession, there is also occasional consensus. In this blog post, we examine a simple rule in web application development:

Separate as much application logic from the HTML code as you can!

Why Do You Need Server-Side Templates?

To explain why it’s considered bad practice to mix HTML with application logic, consider the following example. Let’s say you want to serve the following code to your users:

This is not just some static HTML code. The username is taken from a cookie and is automatically filled in for you. That way you don’t have to type it in if you have previously logged into this website. But that is also a problem. You have to somehow insert the value into the HTML file. There’s a right and a wrong way to do this. But, first, let’s start by asking why you’d do this.

Below is a screenshot depicting a completely wrong approach to solve that problem.

There are many issues with this code and not only because the author didn’t even try to sanitize the input. HTML and PHP are mixed in a convoluted, almost incomprehensible, way. Parts of the HTML code are spread over several functions. However, the real struggle begins when you try to change anything in the HTML code – like adding CSS classes or changing the order of the HTML tags.

This example is obviously deliberately badly written and it could be better formatted. But, in large code bases especially, even well formatted code that is written in a similar way quickly becomes unmanageable. This is why we need templates.

What Are Server Side Templates?

Server Side Templates provide an easier method of managing the dynamic generation of HTML code than the mess we have described above. The big advantage is that you can generate dynamic HTML pages that, on the server side, read like static HTML. Let’s see how our convoluted code looks when we use Server Side Templates.

This is a big improvement on the previous code. However, it’s still static. In order to display the correct information instead of the curly bracket placeholders, we need a template engine that replaces them. The code may look like this on the backend.

It’s immediately clear what this code does. It loads the login.tpl file and assigns variables that match the name of the ones in the template (the one with the curly brackets). Then, using the show function, it replaces them accordingly and prints the HTML code. However, we have added additional functionality to the template which can reveal to the user how long the template took to render.

Why Server Side Templates Can Be Dangerous

It looks like pretty harmless functionality and it actually is. But if you study it again, you can see that it is possible to execute native functions from within the template. This implies that if attackers are able to write such an expression into a template file, they can effectively execute arbitrary functions. But it doesn’t necessarily need to be a file that contains your template. Under the hood, a template engine would convert the file to a string anyway in order to replace the expressions with their result. That’s why many template engines also enable you to pass a string to them instead of the location of a file.

You can compare this to require() and eval() functions. require() includes the file and executes it; eval() executes a string instead of a file. You probably know that it’s a bad idea to pass unsanitized input to eval(). Every decent book about programming should mention this at least a dozen times. But this is often not considered when you deal with a template engine. That means sometimes you can see code like this.

This code shows that we have user-controlled input inside a template string, which in turn means that users can execute template expressions. An example of a malicious expression could be as simple as {{system(‘whoami’)}}, which would execute the whoami system command. Therefore, a template injection can easily lead to a Remote Code Execution, in the same way that passing unsanitized input to the eval function does. This is what we call a Server-Side Template Injection (SSTI). This is a pretty obvious example, but bugs can be even more subtle, for example by concatenating many different components of an application together before passing them to the template engine and by forgetting that some of them may contain user-controllable input.

How Can I Protect Web Applications From Server-Side Template Injections?

  • In order to protect against this kind of vulnerability, you should treat the string loading functionality with the same care as the eval function. Wherever possible, load static template files.
  • But beware: we have already established that this functionality is similar to a require function call. Therefore you should protect against Local File Inclusion (LFI) vulnerabilities here as well. This means that you should not allow users to control the path to such a file, or its content.
  • In addition, whenever you need to pass dynamic data to a template, don’t do it directly within the template file, but use the template engine’s built-in functionality to expand expressions to their result instead.

To determine whether your application is susceptible to SSTI, scan your website today using the Netsparker Web Application Security Scanner.

Sven Morgenroth

About the Author

Sven Morgenroth - Senior Security Engineer