HIGH server side template injectionaxumapi keys

Server Side Template Injection in Axum with Api Keys

Server Side Template Injection in Axum with Api Keys — how this specific combination creates or exposes the vulnerability

Server Side Template Injection (SSTI) occurs when an attacker can inject template expressions that are later evaluated by a server-side templating engine. In Axum, this risk can emerge when Api Keys are used for authorization but the application passes untrusted input into a template rendering step, such as with Askama or similar engines. If user-controlled data (e.g., a query parameter, header, or JSON field) is included in the template context and not properly sanitized, an attacker may craft payloads that execute arbitrary template logic, leading to information disclosure or code execution.

Consider an endpoint that retrieves an Api Key from a request header and then renders it inside a template for debugging or logging purposes:

use axum::{{
    routing::get,
    Router,
    extract::State,
    http::HeaderMap,
}};
use askama::Template;

#[derive(Template)]
#[template(path = "key_status.html")]
struct KeyStatusTemplate {
    api_key: String,
}

async fn status_handler(
    headers: HeaderMap,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    let api_key = headers.get("X-API-Key")
        .map(|v| v.to_str().unwrap_or("").to_string())
        .unwrap_or_else(|| "missing".to_string());
    let template = KeyStatusTemplate { api_key };
    let rendered = template.render().map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
    Ok(Response::builder().body(rendered).unwrap())
}

If the template key_status.html directly interpolates {{ api_key }} without escaping, an attacker supplying {{7*7}} or a more advanced payload could trigger unintended behavior in the templating engine. In Askama, which compiles templates at build time, basic escaping is enforced for variables marked with {{ }}, but if the developer disables autoescape or uses raw blocks, the risk increases. Moreover, if the Api Key is used to dynamically select a template or influence control flow (e.g., conditional blocks based on key validity), injection could allow an attacker to access other templates or sensitive sections of the rendered output. This is especially dangerous when combined with verbose error messages that reveal filesystem paths or template internals, aiding further exploitation.

The presence of Api Keys does not cause SSTI by itself, but it can increase the impact: an exposed key might be used to impersonate services or access downstream systems. Because SSTI in Axum often stems from improper handling of user data in template contexts rather than the key validation logic, the vulnerability remains in the rendering layer. The combination of per-request Api Key extraction and insufficient input sanitization creates a scenario where an authenticated-looking request can abuse the template engine, making detection harder for scanners that do not test authorization boundaries in rendering paths.

Api Keys-Specific Remediation in Axum — concrete code fixes

To mitigate SSTI in Axum when using Api Keys, ensure that user input never reaches the template engine as executable logic. Treat headers, query parameters, and JSON bodies as untrusted, and apply strict validation and escaping. Below are concrete, safe patterns.

1. Strict header validation and no template injection

Do not pass raw header values into templates. Instead, validate the Api Key against a known set or pattern, and only pass safe metadata to the template.

use axum::{{
    extract::Header,
    routing::get,
    Router,
    response::Html,
}};
use askama::Template;

#[derive(Template)]
#[template(path = "key_status_safe.html")]
struct KeyStatusSafeTemplate {
    is_valid: bool,
}

async fn status_handler_safe(
    Header(headers): Header<_, _>,
) -> Result<Html<String>, (StatusCode, String)> {
    let api_key = headers.get("X-API-Key")
        .and_then(|v| v.to_str().ok())
        .filter(|&k| k.len() == 32 && k.chars().all(|c| c.is_ascii_alphanumeric()))
        .is_some();
    let template = KeyStatusSafeTemplate { is_valid: api_key };
    let rendered = template.render().map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
    Ok(Html(rendered))
}

In this approach, the raw key is never placed into the template context. The template only receives a boolean, eliminating any possibility of template injection via the key value.

2. Use autoescape and avoid raw blocks

When you must include user data, configure your template engine to autoescape by default and avoid raw blocks. For Askama, ensure your templates use the default autoescape behavior:

<!-- key_status.html -->
<p>Key status: {{ api_key_status }}</p>

Even if api_key_status contains untrusted strings, Askama will HTML-escape them, neutralizing injection vectors. Do not use {%- raw %}...{% endraw %} blocks with untrusted input.

3. Centralized validation middleware

Move key validation into middleware so templates only see verified, minimal data:

use axum::{{
    extract::Extension,
    middleware::Next,
    body::Body,
    http::Request,
    response::Response,
}};
use std::sync::Arc;

async fn api_key_middleware(
    Extension(state): Extension<Arc<AppState>>,
    mut req: Request<Body>,
    next: Next<Body>
) -> Response {
    if let Some(key) = req.headers().get("X-API-Key") {
        if is_valid_key(key, &state.valid_keys) {
            return next.run(req).await;
        }
    }
    Response::builder().status(StatusCode::UNAUTHORIZED).body(Body::empty()).unwrap()
}

With this pattern, templates receive only pre-validated flags or sanitized representations, and SSTI risks are minimized. The middleware approach also aligns with the principle that Api Key handling should be separate from presentation logic.

Frequently Asked Questions

Can SSTI occur if I only use Api Keys for authentication and not in templates?
Yes, if your templates ever incorporate any user-controlled data—such as request parameters, headers, or dynamic configuration influenced by the key—SSTI remains possible. The vulnerability is about how data reaches the template, not solely about the key's purpose.
Does enabling autoescape in Askama fully prevent SSTI with Api Keys?
Autoescape significantly reduces risk by escaping HTML output, but it does not eliminate all SSTI vectors. If the template uses raw blocks or if the engine is misconfigured, injection may still occur. Combine autoescape with strict input validation and avoid injecting keys into template logic.