Server Side Template Injection in Gin
How Server Side Template Injection Manifests in Gin
Server Side Template Injection (SSTI) in Gin applications occurs when user input is improperly passed to template rendering engines, allowing attackers to inject malicious template code that executes on the server. Gin's flexible template system, while powerful, can become a security liability when template data comes from untrusted sources.
The most common Gin SSTI vulnerability pattern looks like this:
r := gin.Default()
// VULNERABLE: User input directly passed to template
r.GET("/profile", func(c *gin.Context) {
username := c.Query("username")
// User controls the entire template content
c.HTML(http.StatusOK, "templates/user-profile.html", gin.H{
"templateData": username, // Attacker controls template source
})
})An attacker could request /profile?username={{.execve "ls" .args}} to execute arbitrary commands if the template engine evaluates this code.
More subtle vulnerabilities occur when user data populates template fields that themselves contain template syntax:
r.GET("/search", func(c *gin.Context) {
query := c.Query("q")
// User input embedded in template context
c.HTML(http.StatusOK, "templates/search.html", gin.H{
"searchResults": results,
"userQuery": query, // If query contains {{...}}, it executes
})
})Gin's default template engine (html/template) does provide some protection by default, but vulnerabilities emerge when:
- Developers use
{{.}}or{{printf "%s" .}}with user-controlled data - Custom template functions are registered that expose dangerous operations
- Third-party template engines (like Ace or Pongo2) are used without proper sandboxing
- Template inheritance or partials are used with user-controlled names
A particularly dangerous Gin pattern is registering custom template functions:
r.SetFuncMap(template.FuncMap{
"exec": exec.Command, // EXTREMELY DANGEROUS
"readFile": ioutil.ReadFile,
})With this setup, an attacker can execute {{exec "cat" "/etc/passwd"}} through template injection.
Gin-Specific Detection
Detecting SSTI in Gin applications requires both static analysis and dynamic testing. For static detection, look for these Gin-specific patterns:
// Patterns to search for in your codebase
// 1. Direct user input to template rendering
c.HTML(..., gin.H{"templateData": c.Query("input")})Dynamic detection with middleBrick specifically tests for SSTI by:
- Scanning all exposed endpoints for template rendering patterns
- Injecting test payloads like
{{7*7}},{{.}}, and{{exec.Command}}into template contexts - Analyzing responses for template evaluation indicators (42, server errors, or unexpected output)
- Checking for custom template functions that might expose dangerous operations
middleBrick's black-box scanning approach tests your running Gin application without requiring source code access. It sends specially crafted requests to template endpoints and analyzes the responses for signs of successful template injection.
For comprehensive detection, combine middleBrick's automated scanning with manual testing:
# Install middleBrick CLI
npm install -g middlebrick
# Scan your Gin API
middlebrick scan https://your-gin-app.com
# The scan tests for:
# - Template injection in HTML responses
# - Custom template function exposure
# - Unsafe template evaluation
# - Path traversal in template loading
middleBrick specifically checks Gin applications for the 12 security categories including Authentication bypass, BOLA/IDOR, and Property Authorization issues that often accompany SSTI vulnerabilities when combined with other weaknesses.
Gin-Specific Remediation
Remediating SSTI in Gin applications requires a defense-in-depth approach. The most effective strategy combines input validation, secure template practices, and proper function registration.
First, always validate and sanitize user input before template rendering:
func sanitizeInput(input string) string {
// Remove template syntax
re := regexp.MustCompile(`{{.*?}}`)
cleaned := re.ReplaceAllString(input, "")
// Escape HTML to prevent XSS (which often accompanies SSTI)
return html.EscapeString(cleaned)
}
// Secure usage
r.GET("/profile", func(c *gin.Context) {
username := sanitizeInput(c.Query("username"))
c.HTML(http.StatusOK, "templates/user-profile.html", gin.H{
"username": username, // Now safe
})
})For custom template functions, implement a strict whitelist approach:
// Only allow safe functions
func safeFuncMap() template.FuncMap {
return template.FuncMap{
"formatDate": func(t time.Time) string {
return t.Format("2006-01-02")
},
"uppercase": strings.ToUpper,
// No exec, file operations, or dangerous functions
}
}
r.SetFuncMap(safeFuncMap())When using user-controlled template names or paths, validate against a whitelist:
var allowedTemplates = map[string]bool{
"profile": true,
"dashboard": true,
"settings": true,
}
r.GET("/template", func(c *gin.Context) {
templateName := c.Query("template")
if !allowedTemplates[templateName] {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid template"})
return
}
c.HTML(http.StatusOK, templateName+".html", data)
})For high-security applications, consider using template sandboxing:
// Create a sandboxed template executor
func executeTemplateSafely(name string, data interface{}) (string, error) {
tmpl, err := template.New(name).ParseFiles("templates/" + name + ".html")
if err != nil {
return "", err
}
var buf bytes.Buffer
err = tmpl.Execute(&buf, data)
return buf.String(), err
}
// This prevents access to the global template namespace
r.GET("/safe-template", func(c *gin.Context) {
content := executeTemplateSafely("safe", gin.H{"data": "user input"})
c.String(http.StatusOK, content)
})middleBrick's scanning can verify these remediations by testing whether your protected endpoints still respond to SSTI payloads, ensuring your fixes are effective.