Cross-site scripting (XSS) from unescaped data in http.ResponseWriter.Write in net/http

High Risk cross-site-scripting
gonet/httpresponsewriterxssweb

What it is

XSS vulnerability in Go net/http applications where untrusted data is written directly to http.ResponseWriter.Write without HTML escaping, bypassing html/template auto-encoding and allowing malicious scripts to execute in user browsers.

package main

import (
    "net/http"
)

func greetingHandler(w http.ResponseWriter, r *http.Request) {
    name := r.URL.Query().Get("name")
    
    // VULNERABLE: Direct write without escaping
    w.Header().Set("Content-Type", "text/html")
    w.Write([]byte("<html><body>"))
    w.Write([]byte("<h1>Hello " + name + "!</h1>"))
    w.Write([]byte("<p>Welcome to our site.</p>"))
    w.Write([]byte("</body></html>"))
}

func errorHandler(w http.ResponseWriter, r *http.Request) {
    errorMsg := r.URL.Query().Get("error")
    
    // VULNERABLE: User input directly written to response
    w.Header().Set("Content-Type", "text/html")
    w.Write([]byte("<div class='error'>Error: " + errorMsg + "</div>"))
}

// Attack: /?name=<script>alert('XSS')</script>
// Attack: /error?error=<img src=x onerror=alert('XSS')>
package main

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

var greetingTemplate = template.Must(template.New("greeting").Parse(`
<!DOCTYPE html>
<html>
<head>
    <title>Greeting Page</title>
</head>
<body>
    <h1>Hello {{.Name}}!</h1>
    <p>Welcome to our site.</p>
    {{if .Error}}
        <div class="error">Error: {{.Error}}</div>
    {{end}}
</body>
</html>
`))

func greetingHandler(w http.ResponseWriter, r *http.Request) {
    name := r.URL.Query().Get("name")
    errorMsg := r.URL.Query().Get("error")
    
    // SECURE: Use template with automatic escaping
    data := struct {
        Name  string
        Error string
    }{
        Name:  name,     // Automatically HTML-escaped
        Error: errorMsg, // Automatically HTML-escaped
    }
    
    w.Header().Set("Content-Type", "text/html")
    if err := greetingTemplate.Execute(w, data); err != nil {
        log.Printf("Template execution error: %v", err)
        http.Error(w, "Internal server error", 500)
    }
}

💡 Why This Fix Works

The vulnerable code writes untrusted user input directly to http.ResponseWriter without HTML escaping, allowing XSS attacks. The secure version uses html/template which automatically HTML-escapes all data fields, preventing script injection while maintaining normal HTML functionality.

Why it happens

User-controlled data is written directly to ResponseWriter without HTML escaping.

Root causes

Direct Write of Untrusted Data

User-controlled data is written directly to ResponseWriter without HTML escaping.

Bypassing Template Auto-Encoding

Using ResponseWriter.Write instead of html/template bypasses built-in XSS protections.

Fixes

1

Use html/template with Auto-Escaping

Replace direct ResponseWriter.Write calls with html/template.Execute for automatic HTML escaping.

2

Manual HTML Escaping for Raw Strings

If ResponseWriter.Write is necessary, manually escape all untrusted content with html.EscapeString.

3

Use JSON for API Responses

For API endpoints, serialize data with encoding/json and set appropriate Content-Type headers.

Detect This Vulnerability in Your Code

Sourcery automatically identifies cross-site scripting (xss) from unescaped data in http.responsewriter.write in net/http and many other security issues in your codebase.