Prevention of server-side template injection attacks
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.
[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.