HIGH server side template injectionrocketfirestore

Server Side Template Injection in Rocket with Firestore

Server Side Template Injection in Rocket with Firestore — how this specific combination creates or exposes the vulnerability

Server Side Template Injection (SSTI) occurs when user-controlled input is merged into a template that is later interpreted as code by the rendering engine. In Rocket applications that use Firestore as a backend, the risk arises when query parameters, path segments, or request bodies are passed into templates that are processed with a macro engine such as Tera, or when response data from Firestore is directly embedded into templates without proper escaping.

Firestore documents often contain user-supplied fields such as display names, metadata, or configuration values. If these values are rendered into a Rocket template using an unsafe rendering pattern (for example, using unsafe_filter or concatenating strings into a template context), an attacker may be able to inject template directives. In Tera, this can manifest through variable interpolation or filter abuse that leads to arbitrary code execution within the template context.

Consider a Rocket route that retrieves a user profile document from Firestore and renders it with Tera:

use rocket::get;
use rocket::serde::json::Json;
use rocket_ttera::Tera;
use firestore::FirestoreDb;

#[get("/profile<username>")]
async fn profile(username: String, tera: &Tera, db: &State<FirestoreDb>) -> String {
    let db = db.client();
    let user_doc = db.get(&format!("users/{}", username)).await.unwrap();
    let context = json!({
        "username": user_doc.get_string("username").unwrap_or(&username),
        "bio": user_doc.get_string("bio").unwrap_or(&"".to_string()),
    });
    tera.render("profile.html", &context).unwrap_or_default()
}

If the bio field stored in Firestore contains a Tera template expression such as {{ 21 * 2 }} or {% import super %}..., and the template does not sanitize or escape this input, the injected expression may be evaluated during rendering. This can lead to unauthorized operations, information disclosure, or further exploitation depending on the capabilities of the template engine.

Additionally, if the application dynamically constructs template names or paths using Firestore data (for example, selecting a template based on a user role stored in the database), an attacker may be able to traverse directories or load unintended templates, effectively turning a data store into a vector for template manipulation.

The combination of Firestore’s flexible schema and Rocket’s strong typing does not inherently prevent SSTI; it only shifts the responsibility to developers to validate, escape, and sandbox any data that influences template rendering. Relying on Firestore’s access controls or security rules does not mitigate template-level injection if the data is trusted by the rendering layer.

Firestore-Specific Remediation in Rocket — concrete code fixes

Remediation focuses on ensuring that Firestore data is treated as untrusted input and never directly interpolated into templates. Use contextual autoescaping, strict data validation, and avoid dynamic template selection based on Firestore content.

1. Use Tera’s autoescape features and strict variable filtering:


<h1>{{ username | e }}</h1>
<p>{{ bio | e }}</p>

Enable autoescape in your Tera configuration so that variables are HTML-escaped by default:

let tera = Tera::builder()
    .autoescape_on(vec!["**.html"])
    .build("templates/**/*")?;

2. Validate and sanitize Firestore fields before passing them to the template:

use validator::Validate;

#[derive(Validate)]
struct SafeProfile {
    #[validate(length(min = 1, max = 64))]
    username: String,
    #[validate(length(max = 500))]
    bio: String,
}

async fn safe_profile(username: String, tera: &Tera, db: &State<FirestoreDb>) -> Result<String, &'static str> {
    let db = db.client();
    let user_doc = db.get(&format!("users/{}", username)).await.map_err(|_| "db_error")?;
    let profile = SafeProfile {
        username: user_doc.get_string("username").ok_or("missing_field")?.to_string(),
        bio: user_doc.get_string("bio").unwrap_or("".to_string()),
    };
    profile.validate().map_err(|_| "validation_failed")?;
    let context = json!({
        "username": profile.username,
        "bio": profile.bio,
    });
    Ok(tera.render("profile.html", &context).map_err(|_| "render_error")?)}

3. Avoid dynamic template selection based on Firestore data. If you must use multiple templates, use a strict allowlist:

let allowed = ["default.html", "admin.html"];
let template_name = if user_role == "admin" && allowed.contains(&role_template) {
    role_template
} else {
    "default.html"
};
tera.render(template_name, &context)?;

4. Use Rocket request guards to sanitize path parameters before they reach Firestore:

use rocket::request::FromParam;

#[derive(Debug)]
enum SafeUsername(String);

#[rocket::async_trait]
impl<'r> FromParam<'r> for SafeUsername {
    type Error = &'r str;

    fn from_param(param: &'r str) -> rocket::request::Outcome<SafeUsername, &'r str> {
        if param.chars().all(|c| c.is_alphanumeric() || c == '_' || c == '-') {
            rocket::request::Outcome::Success(SafeUsername(param.to_string()))
        } else {
            rocket::request::Outcome::Error(("invalid_username", "Bad Request"))
        }
    }
}

By combining these practices, Rocket applications can safely integrate Firestore while minimizing the attack surface for SSTI and related injection issues.

Frequently Asked Questions

Does using Firestore security rules prevent template injection in Rocket?
No. Firestore security rules control database access but do not affect how data is rendered in application templates. SSTI must be addressed at the template layer through escaping, validation, and safe rendering practices.
Can middleBrick detect SSTI in Rocket and Firestore APIs?
middleBrick scans unauthenticated attack surfaces and can identify indicators such as exposed template endpoints or misconfigured inputs that may lead to SSTI. Findings include severity, guidance, and mapping to frameworks like OWASP API Top 10, but middleBrick does not fix or patch vulnerabilities.