Server Side Template Injection in Rocket with Hmac Signatures
Server Side Template Injection in Rocket with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Server Side Template Injection (SSTI) in the Rocket framework occurs when an attacker can control template input that is later rendered without proper escaping or isolation. When Hmac Signatures are used to bind or validate data that is then passed into a template, a weak implementation can turn the signature mechanism into an indirect injection channel.
Consider a scenario where a Rocket handler builds a context map from query parameters, includes a user-controlled value, and signs the context with an Hmac using a shared secret. If the handler then renders the context with a template engine (for example using the Tera templating engine integrated with Rocket), the signature may be treated as trusted data rather than verified input. An attacker could supply a payload such as {{7*7}} in a parameter, and if the application embeds that parameter into the template context before verification or alongside the signed data, the template engine may execute the injected code during rendering.
Hmac Signatures are typically intended for integrity and authenticity, not for sanitization. If the application validates the Hmac after assembling the data structure that includes user input, the validation may pass while the template still processes malicious content. For example, concatenating user input into a canonical string before signing can be brittle; if the canonicalization excludes certain fields or is performed inconsistently, the signature no longer guarantees safety of the rendered output.
In Rocket, a vulnerable pattern might look like building a map and inserting user values before verifying the Hmac, or including unchecked request data in the template render call. Even when the Hmac is verified, developers may mistakenly assume that verification alone prevents injection, which is not the case. SSTI in this context is about the trust placed in data that has been signed but not properly escaped or sandboxed before template evaluation.
Real-world impacts include unauthorized code execution in the application context, information disclosure via template variables, and potential chaining with other weaknesses such as Path Traversal if the template has access to filesystem helpers. This specific combination is notable because Hmac Signatures can give a false sense of security if the signature logic and template rendering boundaries are not strictly separated.
Hmac Signatures-Specific Remediation in Rocket — concrete code fixes
Remediation focuses on ensuring that user-controlled data is never directly embedded into templates, even after Hmac validation. Keep the signature scope limited to integrity checks and perform output encoding or use type-safe templates to prevent injection.
Example: Safe handling with Rocket and Tera
Use strong separation between signed metadata and user content. Sign only the integrity-critical fields, and treat all user input as untrusted when rendering.
use rocket::serde::{Deserialize, Serialize};
use rocket::response::content::Html;
use rocket_dyn_templates::Template;
use hmac::{Hmac, Mac};
use sha2::Sha256;
type HmacSha256 = Hmac<Sha256>;
#[derive(Serialize, Deserialize)]
struct SafeContext {
user_message: String, // user input, not signed
nonce: u64, // signed metadata
}
#[get("/greet")]
fn greet(
query: <rocket::request::Query<std::collections::HashMap<String, String>>>
) -> Html<String> {
let user_message = query.get("msg").unwrap_or("Hello").to_string();
let nonce: u64 = 12345; // example static nonce
// Build context for rendering — keep user input separate
let context = SafeContext {
user_message: html_escape::encode_text(&user_message).to_string(),
nonce,
};
// Sign only the nonce (or other integrity-critical fields)
let mut mac = HmacSha256::new_from_slice(b"super-secret-key")
.expect("HMAC can take key of any size");
mac.update(serde_json::to_string(&nonce).unwrap().as_bytes());
let result = mac.finalize();
let signature = hex::encode(result.into_bytes());
// Pass only safe, escaped data to the template
rocket_dyn_templates::Template::render("greet", &context)
.map(|body| Html(body))
.unwrap_or_else(|_| Html("Error".to_string()))
}
In this example, user input is HTML-escaped before being placed into the struct that is passed to the template. The Hmac is computed only over the nonce, which does not include the attacker-controlled content. This ensures that signature validation and template rendering operate on disjoint data domains.
Validation and canonicalization discipline
When you must sign structures that include user data, canonicalize consistently and escape on output. Avoid building strings for signing by concatenating loosely; prefer deterministic serialization and validate that no extra fields are introduced between signing and rendering.
use rocket::serde::json::Value;
use rocket_dyn_templates::Template;
// Bad: concatenation prone to canonicalization issues
// let to_sign = format!("{}:{}", user_id, secret);
// Good: use a serialization-based canonical form for signing
let payload = serde_json::json!({ "user_id": user_id, "version": 1 });
let to_sign = serde_json::to_string(&payload).unwrap();
// compute Hmac on `to_sign` ...
// Later, render with explicit escaping
#[derive(Template)]
#[template(path = "display.html.tera")]
struct DisplayContext {
user_id: String, // ensure this is escaped in the template
}
Additionally, audit your templates to ensure they do not use unsafe filters or auto-escaping overrides. Configure Tera to escape by default and avoid marking user input as safe unless it has been explicitly sanitized. These practices prevent SSTI even when Hmac Signatures are used elsewhere in the request lifecycle.