Skip to main content

Server side template injection

Need

Prevention of server-side template injection attacks

Context

  • Usage of Node.js v14.0.0 for server-side JavaScript development
  • Usage of Express for building web applications and APIs
  • Usage of child_process for executing external commands and managing child processes

Description

Non compliant code

import express from 'express';
import { exec } from 'child_process';

const app = express();

app.get('/template', (req, res) => {
const template = req.query.template; // User-controlled input

// Vulnerable code: directly injecting user input into the template
const command = `echo ${template}`;

exec(command, (error, stdout, stderr) => {
if (error) {
console.error(`Error executing command: ${error}`);
return res.status(500).send('Internal Server Error');
}

res.send(stdout);
});
});

app.listen(3000, () => {
console.log('Server is running on port 3000');
});

The vulnerability in this code is a server-side template injection. It occurs because the user-controlled input from the template query parameter is directly injected into the command template without proper sanitization.

In the vulnerable code, the user input is assigned to the template variable. Then, the exec function from the child_process module is used to execute a command. The command is constructed by directly injecting the user input into the template using string interpolation.

This means that an attacker can manipulate the template query parameter to inject malicious code that will be executed on the server side. The impact of this vulnerability can be severe, as it allows the attacker to execute commands remotely, view sensitive files, obtain sensitive information, read system files, and cause strange behavior on the server.

To fix this vulnerability, it is important to properly sanitize user-controlled inputs before using them in command templates. Additionally, it is recommended to use sandboxing techniques to limit the capabilities of the executed commands and mitigate the potential impact of an exploitation.

Steps

  • Sanitize user-controlled input to prevent any malicious code injection.
  • Use parameterized queries or prepared statements when constructing commands or queries that involve user-controlled input.
  • Avoid directly injecting user input into command templates or queries.
  • Implement proper input validation and filtering to ensure that only expected values are accepted.
  • Encourage the use of sandboxing techniques to limit the impact of any potential code injection.
  • Regularly update and patch dependencies to mitigate any known vulnerabilities.

Compliant code

import express from 'express';
import { exec } from 'child_process';

const app = express();

app.get('/template', (req, res) => {
const template = sanitizeInput(req.query.template); // Sanitize user-controlled input

// Construct the command using parameterized queries or prepared statements
const command = `echo ${template}`;

exec(command, (error, stdout, stderr) => {
if (error) {
console.error(`Error executing command: ${error}`);
return res.status(500).send('Internal Server Error');
}

res.send(stdout);
});
});

app.listen(3000, () => {
console.log('Server is running on port 3000');
});

function sanitizeInput(input: string): string {
// Implement proper input validation and filtering
// Sanitize the input to prevent any malicious code injection
// You can use a library or custom logic to sanitize the input
// Example: return input.replace(/[^\\w\\s]/gi, '');
return input;
}

The fixed code addresses the vulnerability by implementing proper input sanitization and using parameterized queries or prepared statements to construct the command.

Here's how the code works:

  1. The code imports the necessary modules, including the express framework and the exec function from the child_process module.

  2. An instance of the express application is created.

  3. The code defines a route handler for the /template endpoint using the app.get method. This endpoint expects a query parameter called template.

  4. The sanitizeInput function is called to sanitize the user-controlled input received in the template query parameter. This function should implement proper input validation and filtering to prevent any malicious code injection. In this example, it simply returns the input as is, but you should replace this with your own sanitization logic.

  5. The command is constructed using the sanitized template value. It is important to note that this code is just an example and should not be used in production. In a real-world scenario, you should use parameterized queries or prepared statements to construct the command, which helps prevent code injection vulnerabilities.

  6. The exec function is called with the constructed command. This function executes the command on the server-side.

  7. In the callback function of the exec function, any errors that occur during command execution are logged to the console, and an appropriate error response is sent if an error occurs.

  8. If the command executes successfully, the response containing the output of the command is sent back to the client.

  9. The app.listen method is called to start the server on port 3000.

Overall, the fixed code demonstrates the importance of properly sanitizing user-controlled inputs and using secure methods to construct commands to mitigate the risk of server-side template injection vulnerabilities.

References