Cross-Site Scripting via template.HTML in Go

High Risk Cross-Site Scripting (XSS)
goxsstemplatehtmlfmt-sprintf

What it is

XSS vulnerabilities occur when formatted strings containing user input are passed to template.HTML, which bypasses Go's automatic HTML escaping. This allows attackers to inject malicious scripts that execute in users' browsers, steal sessions, manipulate DOM content, or perform unauthorized actions.

package main

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

func profileHandler(w http.ResponseWriter, r *http.Request) {
    username := r.URL.Query().Get("username")
    
    // VULNERABLE: template.HTML bypasses escaping
    htmlContent := template.HTML(fmt.Sprintf("<h1>Welcome %s!</h1>", username))
    
    tmpl := `<html><body>{{.Content}}</body></html>`
    t := template.Must(template.New("profile").Parse(tmpl))
    
    data := struct{ Content template.HTML }{htmlContent}
    t.Execute(w, data)
}

// Attack: username = "<script>alert('XSS')</script>"
// Result: <h1>Welcome <script>alert('XSS')</script>!</h1>
// This executes the attacker's script
package main

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

func profileHandler(w http.ResponseWriter, r *http.Request) {
    username := r.URL.Query().Get("username")
    
    // SECURE: Let template handle escaping automatically
    tmpl := `<html><body><h1>Welcome {{.Username}}!</h1></body></html>`
    t := template.Must(template.New("profile").Parse(tmpl))
    
    data := struct{ Username string }{username}
    t.Execute(w, data)
}

💡 Why This Fix Works

The vulnerable code uses template.HTML with fmt.Sprintf(), bypassing automatic HTML escaping and allowing script injection. The secure version passes the username string directly to the template, letting html/template automatically escape special characters.

Why it happens

Formatting strings with user input then passing to template.HTML.

Root causes

Using template.HTML with fmt.Sprintf

Formatting strings with user input then passing to template.HTML.

String Concatenation Before template.HTML

Concatenating user input into strings before marking as template.HTML.

Disabling Automatic Escaping

Using template.HTML to bypass automatic escaping for dynamic content.

Fixes

1

Use Automatic Template Escaping

Let html/template automatically escape data by passing strings directly to templates.

2

Use Bluemonday for HTML Sanitization

If HTML rendering is required, sanitize with bluemonday package before using template.HTML.

3

Only Use template.HTML with Constants

Reserve template.HTML for trusted, hardcoded content only.

Detect This Vulnerability in Your Code

Sourcery automatically identifies cross-site scripting via template.html in go and many other security issues in your codebase.