For developers building modern web applications, Server-Side Template Injection (SSTI) is a critical vulnerability they should be aware of and carefully avoid. If left unchecked, this vulnerability can lead to significant security issues. SSTI arises when applications unintentionally allow user input to be interpreted as code within server-side templates, creating a potential avenue for attackers to execute malicious actions.
In this blog, we'll dive into Server-Side Template Injection (SSTI) vulnerabilities, exploring their causes and how to identify them within web application code. We'll then use StackHawk to analyze a vulnerable Flask application, pinpoint the SSTI vulnerability, and show you how to fix it. Finally, we'll rescan the application to ensure the vulnerability is resolved. To start, let's clearly understand what an SSTI vulnerability is and why it matters.
What Is Server Side Template Injection (SSTI)?
Server-Side Template Injection (SSTI) poses a significant risk to the security of web applications. This vulnerability arises when an application incorporates user-supplied input directly into its server-side templates without proper sanitization or validation. Template engines, which combine templates with dynamic data to generate web pages, can be exploited through SSTI attacks.
The typical pattern of an SSTI attack involves an attacker analyzing the application's structure and identifying areas where user input might be inserted into templates. They then attempt to inject malicious code, often using the template engine's native syntax, into the input fields. If the application fails to sanitize or escape this input, the injected code can be executed on the server side. This can lead to various consequences, from data leaks and website defacement to full remote code execution, depending on the specific template engine and application environment.
How Does SSTI Differ From XSS?
SSTI vulnerabilities can be easily confused with Cross-Site Scripting (XSS). While both involve injecting malicious code, they differ fundamentally in their targets and potential impact. XSS exploits vulnerabilities in client-side code, injecting malicious scripts into web pages viewed by other users. On the other hand, SSTI targets the server side, injecting code into the templates used to generate web pages. Here’s a table to make it easier to see the difference between the two types of vulnerabilities.
What Are The Root Causes Of SSTI?
Often, it is a combination of factors that lead to SSTI vulnerabilities, including:
Insufficient Input Sanitization: Failure to properly sanitize and validate user-supplied data before incorporating it into templates allows malicious actors to inject executable code.
Dynamic Template Construction: Constructing templates dynamically based on user input can inadvertently introduce vulnerabilities if not handled carefully.
Misconfigured Template Engines: Improper configuration or misuse of advanced template engine features can expose vulnerabilities and create opportunities for SSTI attacks.
Regardless of the exact factors, the root cause of all Server-Side Template Injection (SSTI) vulnerabilities is inadequate sanitization and validation of user-supplied data before it’s used in server-side templates.
How To Prevent SSTI Vulnerabilities
Knowing SSTI vulnerabilities stem from inadequate handling of user input within templates, a holistic approach to building and maintaining your web applications that include secure coding practices, input validation, and continuous security testing is essential. Here are a few key strategies for minimizing your exposure to SSTI vulnerabilities:
Always Use Input Validation and Sanitization: Treat all user-supplied data as potentially malicious and enforce rigorous validation and sanitization procedures to filter out harmful characters and patterns.
Context-Aware Escaping: Encode special characters using the template engine's native escaping mechanisms, preventing their interpretation as executable code.
Content Security Policy (CSP): Employ CSP headers to restrict the sources from which scripts can be loaded, mitigating the potential impact of SSTI attacks.
Principle of Least Privilege (PoLP): Adhere to the PoLP by granting templates only the minimum necessary permissions required for their intended functionality.
Regular Security Testing: Conduct routine security assessments using tools like StackHawk to proactively identify and address SSTI vulnerabilities before they can be exploited.
Following these practices in your application development and deployment lifecycle will improve your resilience against SSTI attacks. Let’s put this into practice by looking at a vulnerable Flask/Jinja application, using StackHawk to identify the SSTI vulnerability, and helping us verify that the fix we will implement successfully remediates the issue.
Detecting And Fixing SSTI Vulnerabilities In Flask/Jinja (Python)
Building The Vulnerable Endpoint
Below is an example of a simple Flask application with an SSTI vulnerability. This application allows users to input their name, which is then rendered in a greeting message using Jinja2 templating. However, it embeds the user input into the template without proper sanitization or escaping, leading to a potential Server-Side Template Injection (SSTI) vulnerability in Jinja2. This scenario illustrates a simple SSTI vulnerability: improperly sanitized user input injected into a template.
Please note: This is an intentionally vulnerable API for demonstration purposes. Do not use this code in an actual application, as it is not production-ready.
First, create a new directory for your project and navigate into it. If you do this through a terminal, you can run the command below.
mkdir ssti-app-example
cd ssti-app-example
Next, point to the root directory with the terminal and initialize a new Python project/virtual environment using the following command.
python3 -m venv myenv
source myenv/bin/activate
Finally, before we write any code, we will install the following dependency:
Flask: This is used to set up the server (this includes the Jinja2 template engine).
To do this, using the same terminal pointed to the project root, run the following:
pip install Flask
Now, we will begin creating our application. In the project's root directory, create a file named app.py. This file will contain your Flask server and the API endpoint, including the SSTI vulnerability.
After creating and opening the app.py file, add the following code:
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route('/')
def home():
return '''
<form method="post" action="/greet">
<input type="text" name="name" placeholder="Enter your name">
<input type="submit" value="Greet">
</form>
'''
@app.route('/greet', methods=['POST'])
def greet():
name = request.form['name']
template = f"Hello, {name}!"
return render_template_string(template)
if __name__ == '__main__':
app.run(debug=True)
Let’s do a quick run-through of the code.
Initialization of Flask App
app = Flask(__name__)
Initializes the Flask application, setting up the basic environment for the API server.
API Endpoint Definitions
@app.route('/')
defines a route for the home page (/) which returns a basic HTML form that prompts the user to enter their name and submit it via a POST request@app.route('/greet', methods=['POST'])
defines a route for /greet that only accepts POST requests. It uses render_template_string to render the greeting message as HTML.
Running The Code
Now that our code is complete, it's time to demonstrate the SSTI vulnerability. Launch the Flask application using the following command in a terminal pointed to the root of your project with your virtual environment still active:
flask run -p 4000
Note that the provided code directly embeds user input (the name variable) into the template without sanitization, thus rendering it vulnerable to SSTI.
To demonstrate this, we can:
Navigate to the root URL (e.g., http://127.0.0.1:4000/) .
Input “{{ 7*7 }}” into the designated field.
Click the X button.
Instead of displaying the expected "Hello, {{ 7*7 }}!", the application will evaluate the injected expression and display "Hello, 49!"
Although not overly malicious, this demonstrates the presence of the SSTI vulnerability within our code. There is the potential that more malicious code could be executed within this application and cause deeper issues. So, let’s take a look at testing for this vulnerability with StackHawk.
Detecting The Vulnerability With StackHawk And HawkScan
Now that our code is set up and our application is running let’s get StackHawk to identify this vulnerability automatically. To do this, you must ensure you have a StackHawk account. If you need one, you can sign up for a trial account or log into an existing account.
If you’re logging into an existing StackHawk account, you’ll click Add an App -> Create Custom App from the Applications page.
If you’re new to StackHawk, you’ll be automatically brought into the Add an App flow. On the Scanner screen, you’ll see the instructions for installing the StackHawk CLI. Since we will be running our testing locally, we will use this option. Once the hawk init command is executed successfully, click the App Details button.
On the next screen, you will fill out an Application Name, Environment Name, and Host. Feel free to copy the default values I’ve added in the screenshot below for our purposes. Once filled out, click App Type.
To detect the SSTI vulnerability, we will set the Application Type to “Dynamic Web Application/Single Page Application” and the API Type to “Other.” Once complete, click Next.
Lastly, we will need to add a stackhawk.yml file to the root of our project. Once the file is added, copy the screen's contents, paste it into the file, and save it. Lastly, we can click the Finish button.
Enabling SSTI Detection in HawkScan
After creating the app in StackHawk, we need to take a few more steps to enable SSTI detection. Two ways to do this are using the "HawkScan Default" policy or customizing an existing policy.
To set HawkScan up with the "HawkScan Default" policy, you will log into StackHawk, go to the Applications screen, and click on the settings link for your application.
Next, on the Settings screen, under HawkScan Settings, click the Applied Policy dropdown and select the "HawkScan Default" policy.
On the next screen, under the Plugins and Tests section, click on the SSTI entry to enable HawkScan to execute tests for potential SSTI vulnerabilities.
For this example, we will need to make a small change and add the following code to the stackhawk.yml file. This configures StackHawk to the base spider and the Ajax spider to discover routes to test.
hawk:
spider:
base: true
ajax: true
ajaxBrowser: CHROME_HEADLESS
This configures Hawk Scan to use the base spider and the Ajax spider.
Now, with our application set up in StackHawk, our stackhawk.yml added to the root of our project, and SSTI detection enabled, we can finally test our application.
Run HawkScan
To test our application, in a terminal pointing to the root of our project, we will run HawkScan using the following command:
hawk scan
After running the command, the tests should execute in the terminal.
Explore The Initial Findings
Once the tests are complete, the terminal will contain some information about any vulnerabilities found. Below, we can see that it has identified the SSTI vulnerability that we introduced in the code. To explore this further, we will click on the test link at the bottom of the output. This will take us into the StackHawk platform to explore further.
After clicking the link, we can see the test results nicely formatted. Next, we will click the Server Side Template Injection (SSTI) entry.
Within this entry, we can see an Overview and Resources that can help us fix this vulnerability, as well as the Request and Response that the API returned. Above this, you will also see a Validate button, displaying a cURL command with the exact API request used to expose the vulnerability.
Fixing The SSTI Vulnerability
To fix the SSTI vulnerability, avoid using render_template_string with untrusted input. Instead, use render_template with a predefined template. This allows Flask to escape user input automatically, preventing it from being executed as code. To implement this, update your app.py to the following:
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/')
def home():
return '''
<form method="post" action="/greet">
<input type="text" name="name" placeholder="Enter your name">
<input type="submit" value="Greet">
</form>
'''
@app.route('/greet', methods=['POST'])
def greet():
name = request.form['name']
return render_template('greet.html', name=name)
if __name__ == '__main__':
app.run(debug=True)
In this secure version, render_template is used with a separate HTML template file (greet.html). Flask passes the name variable to the template, and Jinja2 automatically escapes it, rendering it safe from injection attacks.
After creating and opening the greet.html file, add the following code:
<html lang="en">
<head>
<meta charset="utf-8">
<title>Greet</title>
</head>
<body>
<h1>Hello, {{ name }}!</h1>
</body>
</html>
By using render_template with predefined templates, we ensure the template context is escaped correctly. This approach separates user input from template code, mitigating the risk of SSTI. This example demonstrates the identification and remediation of an SSTI vulnerability in a Flask and Jinja2 project.
Once the code has been updated on your side, you can stop the currently running server (by using ctrl + C in the terminal) and restart it with the new code using:
flask run –p 4000
The app should now be up and running with the updated code!
Confirm The Fix
We can run the same test as before and enter {{ 7*7 }} into the input field instead of Hello, 49! we now get Hello, {{ 7*7 }}! as expected.
Now, let’s use StackHawk to confirm that it’s fixed. To do this, we will click the Rescan Findings button back in StackHawk.
Then, we will see a modal containing the “hawk rescan” command that includes the correct Scan ID. You’ll run this command in the same terminal where you ran the initial set of tests.
In the output, you will again see any vulnerabilities in the scan. In this case, the SSTI vulnerability is no longer showing. Clicking on the link at the bottom of the terminal output, you can confirm that the SSTI vulnerability has now been added to the Fixed Findings from Previous Scan section, confirming that the vulnerability has been successfully fixed and has passed any vulnerability tests.
With that, we’ve successfully remedied and retested our application to ensure its safety from potential SSTI attacks. Please remember that the code above is for demonstration purposes only. Carefully validating and sanitizing all user input before it's passed to a template engine is one of the best ways to prevent SSTI and secure your applications. Using a library designed to minimize the risk of overlooking potential vulnerabilities is one of the best ways to mitigate them.
Why StackHawk?
When it comes to preventing vulnerabilities, such as SSTI, StackHawk offers a comprehensive platform for developers to secure their applications and APIs. StackHawk is a modern, powerful DAST tool designed for developers to integrate application security testing seamlessly into their software development lifecycle. It is not just another security tool; it's a developer-first platform emphasizing automation, ease of use, and integration into existing development workflows.
At its core, StackHawk is built around empowering developers to take the lead in application security. It provides a suite of tools that make it easy to find, understand, and fix security vulnerabilities before they make it to production. One of the critical components of StackHawk is HawkScan, a dynamic application security testing (DAST) tool that scans running applications and APIs to identify security vulnerabilities.
With StackHawk, developers and security teams can:
Automate Security Testing: Integrate security testing into CI/CD pipelines, ensuring every build is automatically scanned for vulnerabilities.
Identify and Fix Vulnerabilities: Receive detailed, actionable information about each vulnerability, including where it is in the code and how to fix it.
Test in All Environments: Use StackHawk in various environments, including development, staging, and production, to catch security issues at any stage of the development process.
Customize Scans: Tailor scanning configurations to suit specific applications and environments, ensuring more relevant and accurate results.
By focusing on a developer-first approach to security testing and integrating smoothly into existing dev tools and practices, StackHawk demystifies application security, making it a more approachable and manageable part of the software development lifecycle.
Conclusion
In this post, we've explored the critical nature of Server-Side Template Injection (SSTI) vulnerabilities, understanding how they arise from the mishandling of user input within template engines. We've seen their potential for malicious code execution, leading to serious security breaches.
To mitigate these risks, we've emphasized the importance of a robust security strategy, including input validation, secure template engines, and the principle of least privilege. We also demonstrated how StackHawk's dynamic application security testing (DAST) can proactively identify SSTI vulnerabilities, allowing quick remediation. By integrating security best practices and utilizing tools like StackHawk, you can significantly enhance the security posture of your applications, safeguarding them against SSTI attacks. Sign up today to use StackHawk’s modern DAST platform to level up your security game, find SSTI vulnerabilities, and more.