Server Side Template Injection on Azure

How Server Side Template Injection Manifests in Azure

Server Side Template Injection (SSTI) in Azure environments typically occurs when user input is improperly handled by Azure's template engines or when third-party libraries process untrusted data. Azure developers often encounter this in Razor Pages, Blazor components, and Azure Functions that use template rendering.

The most common Azure-specific SSTI vectors appear in:

  • Razor Pages where model binding fails to sanitize HTML content
  • Azure App Service applications using custom template engines
  • Azure Logic Apps with poorly configured workflow definitions
  • Azure Functions processing user-supplied template data

Consider this Azure Razor Page example that's vulnerable to SSTI:

@model MyModel

<div>
    @Html.Raw(Model.UserInput)
</div>

If Model.UserInput contains <script>alert(1)</script>, the script executes. More sophisticated attackers can inject Razor expressions like:

@DateTime.Now.AddDays(1)

This executes server-side and displays tomorrow's date, demonstrating arbitrary code execution capability.

In Azure Functions, SSTI often appears when processing JSON templates:

public static async Task<HttpResponseData> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req,
    FunctionContext executionContext)
{
    var template = await new StreamReader(req.Body).ReadToEndAsync();
    var result = Razor.Parse(template, model); // Unsafe template parsing
    
    return req.CreateResponse(HttpStatusCode.OK);
}

Azure Logic Apps present unique SSTI risks when workflow definitions accept user input:

@{ 
    // User-controlled input reaches template evaluation
    var userInput = workflowContext.inputs['message'];
    
    // Malicious payload: @{ System.Diagnostics.Process.Start("cmd.exe", "/c calc.exe") }
    return userInput;
}

The Azure-specific context means attackers can leverage .NET's reflection capabilities, access Azure Key Vault secrets if the function has permissions, or trigger Azure Service Bus messages to cause side effects.

Azure-Specific Detection

Detecting SSTI in Azure requires understanding both the .NET template engines and Azure's specific execution contexts. Azure developers should implement multiple detection layers:

Static Analysis in Azure DevOps: Configure Azure DevOps pipelines to scan for unsafe template rendering patterns:

- task: DotNetCoreCLI@2
  inputs:
    command: 'custom'
    custom: 'security'
    arguments: '--scan --ruleset:SecurityRules.ruleset'

Runtime Detection with Application Insights: Monitor for template injection patterns in Azure Application Insights:

var telemetry = new TelemetryClient();

// Monitor for suspicious template patterns
telemetry.TrackEvent("TemplateInjectionAttempt", new Dictionary<string, string>
{
    { "Pattern", suspiciousPattern },
    { "Source", request.Path }
});

middleBrick Azure Scanning: middleBrick's Azure-specific checks include:

  • Detection of Razor/RazorLight template engines in Azure Functions
  • Analysis of Logic App workflow definitions for injection points
  • Scanning of Blazor component parameters for unsafe HTML rendering
  • Detection of custom template engines in Azure App Service

When middleBrick scans an Azure endpoint, it tests for SSTI by injecting payloads like:

@DateTime.Now.ToString("yyyy-MM-dd")
@{ System.Diagnostics.Process.GetCurrentProcess().ProcessName }
@new System.Text.StringBuilder().Append("test")

The scanner analyzes responses to determine if template expressions executed, providing a severity score and specific remediation guidance.

Azure Security Center Integration: Configure Azure Security Center to monitor for SSTI-related patterns:

# Azure CLI for security policy
az security policy create \
    --name "SSTI-Detection" \
    --rules "detect-template-injection" \
    --severity High

Azure-Specific Remediation

Remediating SSTI in Azure requires Azure-native solutions that prevent template injection while maintaining functionality. Here are Azure-specific approaches:

Razor Pages with HTML Encoding: Always encode user input unless absolutely necessary:

@model MyModel

<div>
    @Html.Encode(Model.UserInput) " or " @Model.UserInput " for automatic encoding
</div>

Azure Functions Template Security: Use Azure's Safe HTML handling:

using Microsoft.AspNetCore.Html;

public static async Task<HttpResponseData> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req)
{
    var template = await new StreamReader(req.Body).ReadToEndAsync();
    
    // Use Azure's HTML sanitizer
    var sanitizer = new HtmlSanitizer();
    var safeContent = sanitizer.Sanitize(template);
    
    return req.CreateResponse(HttpStatusCode.OK);
}

Blazor Component Security: Use @bind-Attribute with validation:

<InputText @bind-Value="@sanitizedInput" @bind-Value:event="oninput"
           ValueExpression="() => model.Input"
           ValueChanged="HandleInputChanged" />

@code {
    private string sanitizedInput = string.Empty;
    
    private void HandleInputChanged(string newValue)
    {
        // Azure-specific sanitization
        sanitizedInput = System.Net.WebUtility.HtmlEncode(newValue);
        model.Input = sanitizedInput;
    }
}

Azure Logic Apps Protection: Use Azure's built-in expression validation:

// Instead of directly using user input
// Use Azure Logic Apps' secure expression evaluation
@concat('<span>', encodeUriComponent(triggerBody()['message']), '</span>')

Azure App Service Configuration: Configure Azure Web App to block dangerous content types:

# Azure CLI configuration
az webapp config set --resource-group MyResourceGroup \
    --name MyWebApp \
    --use-32-bit-worker-process false \
    --auto-heal-enabled true \
    --auto-heal-rules '{