HIGH server side template injectionginfirestore

Server Side Template Injection in Gin with Firestore

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

Server Side Template Injection (SSTI) occurs when an attacker can inject template directives that are executed by the server-side templating engine. In a Gin application that renders responses using Go templates and passes data from Firestore, SSTI can arise if user-controlled input is inserted into the template context without proper escaping or sandboxing. Go’s html/template package is generally safe, but unsafe practices—such as marking strings as template.HTML or using custom delimiters—can disable escaping and allow injection.

When Firestore documents are directly mapped into template data structures, fields like document titles, descriptions, or user-supplied query parameters may originate from untrusted sources. If these fields are injected into a template and later rendered as part of an expression or function call, an attacker can craft payloads that execute unintended logic. For example, a payload such as {{ (index . "FirestoreData").Execute }} could attempt to invoke methods or access nested fields if the template engine is misconfigured or if unsafe functions are registered.

In Gin, developers sometimes register custom template functions to interact with Firestore, such as formatting timestamps or resolving document references. If these functions are not carefully restricted, an attacker may leverage SSTI to call arbitrary functions, access the underlying context, or probe the runtime environment. Because Firestore data may include dynamic keys or user-generated content, the risk increases when templates are built with partially trusted data. The combination of a flexible templating system, direct binding of Firestore documents, and insufficient input validation creates a pathway for attackers to manipulate template execution.

Real-world attack patterns mirror classic SSTI techniques described in the OWASP API Top 10, particularly under security misconfiguration and injection risks. Although middleBrick does not perform source analysis, its scans can detect indicators such as unsafe template handling or exposed debug endpoints that may facilitate injection. By testing unauthenticated endpoints and analyzing OpenAPI specs alongside runtime behavior, a scanner can highlight mismatches between declared input constraints and actual behavior, helping teams identify where user-supplied data reaches template logic.

Concrete indicators of risk include templates that use non-standard delimiters, registered functions that accept variadic arguments, or routes that pass raw Firestore document maps directly into the rendering pipeline. These patterns do not guarantee exploitation, but they increase the likelihood that an injected template directive can influence execution flow. Proper sanitization, strict context-aware escaping, and avoiding the use of template.HTML on user-controlled strings are essential mitigations.

Firestore-Specific Remediation in Gin — concrete code fixes

To prevent SSTI in Gin applications that use Firestore, ensure that all user-controlled data is treated as untrusted and is never directly embedded into executable template constructs. The following practices and code examples demonstrate secure handling of Firestore data within Go templates.

1. Use html/template with automatic escaping

Always use html/template (not text/template) and avoid marking strings as template.HTML unless you fully control and sanitize the content. Render Firestore document fields as plain text so that characters like <, >, and quotes are escaped.

import (
    "html/template"
    "net/http"

    "github.com/gin-gonic/gin"
    "cloud.google.com/go/firestore"
)

func showDocument(c *gin.Context) {
    client, _ := firestore.NewClient(c, "my-project-id")
    docSnap, _ := client.Collection("docs").Doc(c.Param("id")).Get(c)
    var data map[string]interface{}
    docSnap.DataTo(&data)

    // Safe: data values are passed as plain interface{} and escaped automatically
    t, _ := template.New("doc").Parse(`
      <h1>{{.Title}}</h1>
      <p>{{.Description}}</p>
    `)
    c.Data(http.StatusOK, "text/html", executeTemplate(t, data))
}

func executeTemplate(t *template.Template, data interface{}) []byte {
    // In production, handle errors appropriately
    buf := new(bytes.Buffer)
    t.Execute(buf, data)
    return buf.Bytes()
}

2. Avoid registering unsafe custom functions

If you must register custom functions, restrict them to pure, side-effect-free operations and never allow them to evaluate arbitrary strings as code. Do not expose functions that accept and eval user input.

func safeFormatDate(args ...interface{}) (string, error) {
    if len(args) != 1 {
        return "", fmt.Errorf("expected one argument")
    }
    t, ok := args[0].(time.Time)
    if !ok {
        return "", fmt.Errorf("invalid type")
    }
    return t.Format("2006-01-02"), nil
}

func setupRouter() *gin.Engine {
    r := gin.Default()
    funcMap := template.FuncMap{
        "formatDate": safeFormatDate,
    }
    r.SetFuncMap(funcMap)
    // Templates can use {{ formatDate .CreatedAt }} safely
    return r
}

3. Validate and whitelist Firestore field usage

Do not allow Firestore document keys or dynamic field names to dictate template structure. Instead, map known fields to a strict DTO (Data Transfer Object) and pass only those fields to the template.

type DocumentView struct {
    Title       string
    Description string
    Author      string
}

func getDocumentView(docSnap *firestore.DocumentSnapshot) (*DocumentView, error) {
    var dto DocumentView
    if err := docSnap.DataTo(&dto); err != nil {
        return nil, err
    }
    // Additional validation can be applied here
    return &dto, nil
}

func renderSafe(c *gin.Context) {
    client, _ := firestore.NewClient(c, "my-project-id")
    docSnap, _ := client.Collection("docs").Doc(c.Param("id")).Get(c)
    dto, _ := getDocumentView(docSnap)

    t, _ := template.New("safe").Parse(`
      <h1>{{.Title}}</h1>
      <p>{{.Description}}</p>
    `)
    c.Data(http.StatusOK, "text/html", executeTemplate(t, dto))
}

4. Enforce strict delimiters and disable unknown functions

Ensure that your template parser does not allow function calls or actions that could be abused. Stick to the default delimiters and avoid calling template.JSEscape or similar on user input.

// Use the default {{ }} delimiters and do not change them to obscure strings
// Avoid calling t.ExecuteTemplate with dynamic template names

Frequently Asked Questions

Can SSTI in Gin with Firestore lead to remote code execution?
In Go templates, remote code execution is unlikely due to the language's static typing and lack of direct eval, but SSTI can still lead to data leakage, unauthorized logic execution, or HTML injection if unsafe practices are used.
How can I detect SSTI risks in my Gin and Firestore integration?
Use middleBrick to scan your API endpoints. It runs 12 security checks in parallel, including Input Validation and Unsafe Consumption, and can identify risky patterns such as unescaped template usage or improper handling of Firestore data without requiring credentials.