Skip to main content

Server side template injection

Need

Prevention of server-side template injection attacks

Context

  • Usage of C# for developing robust and efficient applications

Description

Non compliant code

public IActionResult Index(string name)
{
var message = $"Hello {name}";
return View((object)message);
}

The above code is a method in a controller in an ASP.NET Core application. It takes a name parameter from the URL and injects it into a string which is then passed to the View method.

This code is vulnerable to Server Side Template Injection (SSTI) attacks. This vulnerability occurs when an attacker is able to inject template directives which can be executed on the server side.

In this case, an attacker could potentially input a name parameter that includes template syntax. As the name parameter is directly injected into the message string without any sanitization or encoding, this could lead to the execution of arbitrary code on the server side.

For example, an attacker could use the name parameter {{7*'7'}}. This would result in the message string becoming Hello 49, as the injected template syntax {{7*'7'}} is evaluated on the server side.

This could potentially lead to more dangerous attacks, as an attacker could inject template syntax that performs more malicious actions, such as executing system commands or accessing sensitive data.

Steps

  • Sanitize user input: Validate and sanitize the 'name' parameter to prevent any malicious input from being injected into the template.
  • Use a safe template engine: Replace the current template engine with a safe template engine that automatically escapes user input to prevent template injection.
  • Implement a whitelist approach: Instead of allowing arbitrary template syntax, define a whitelist of allowed template expressions and only allow those expressions to be used in the template.
  • Input validation and output encoding: Validate and sanitize all user input throughout the application and properly encode any output that is rendered in the template to prevent injection attacks.
  • Regularly update dependencies: Keep all dependencies, including the template engine, up to date with the latest security patches to mitigate any known vulnerabilities.

Compliant code

[HttpPost]
public IActionResult Index([FromBody] UserInput input)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

var sanitizedInput = HttpUtility.HtmlEncode(input.Name);
var message = $"Hello {sanitizedInput}";
return View((object)message);
}

public class UserInput
{
[Required]
[StringLength(100, ErrorMessage = "Name length can't be more than 100.")]
public string Name { get; set; }
}

The updated code now includes a UserInput class that is used to strongly type the incoming request. This class has a single property, Name, which is validated using data annotations. The Required attribute ensures that the Name property must be present in the request, and the StringLength attribute limits the length of the Name to 100 characters.

The Index method now accepts a UserInput object as a parameter instead of a string. This allows ASP.NET Core's model binding to automatically validate the incoming request based on the data annotations defined in the UserInput class. If the request is invalid, the method immediately returns a BadRequest response that includes the validation errors.

Before the Name property is used to create the message, it is sanitized using the HttpUtility.HtmlEncode method. This method encodes special characters in the string to their HTML-encoded equivalents, which prevents any injected script or markup from being executed.

This approach addresses the server-side template injection vulnerability by validating and sanitizing user input, and by encoding the output that is rendered in the template. It's also recommended to use a safe template engine that automatically escapes user input, implement a whitelist of allowed template expressions, and regularly update all dependencies to mitigate any known vulnerabilities.

References