Link Search Menu Expand Document

Cross-Site Scripting in Go Lang

Vulnerable example

The server() function that handles HTTP GET requests reads the parameter param from the query string and returns it (as is) in the HTTP response. The default Content-Type response header is determined by the http.DetectContentType function which implements the algorithm described by the WhatWG spec.

package main

import "io"
import "net/http"

func server(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, r.URL.Query().Get("param"))
}

func main() {
    http.HandleFunc("/", server)
    http.ListenAndServe(":5000", nil)
}

By sending a payload with param=hello, the browser developer tool shows that the Content-Type is set to text/plain (which is not harmful and rendered as simple text by the browser).

xss1.png

By sending a request with param=<script>alert(1)</script>, the Content-Type of the response is set to text/html, resulting in an exposure to Cross-Site Scripting.

xss2.png

Prevention

The above exposure can be remediated by performing output encoding of the user-controlled parameter. Go’s html/template package includes several output encoding functions.

Context API
HTML HTMLEscapeString
URL Attributes URLQueryEscaper
JavaScript JSEscapeString
CSS CSS

In the previous example, the issue can be remediated by performing Output Encoding of the user supplied input using the HTMLEscapeString function:

import "io"
import "net/http"

func server(w http.ResponseWriter, r * http.Request) {
    encodedParam = template.HTMLEscapeString(r.URL.Query().Get("param"))
    io.WriteString(w, encodedParam)
}

func main() {
    http.HandleFunc("/", server)  http.ListenAndServe(":8080", nil)
}

Vulnerable Example

The server() function that handles HTTP GET requests reads the parameter error from the query string and includes it as part of a template (text/template module). The default Content-Type response header is determined by the http.DetectContentType function that implements the algorithm described by the WhatWG spec.

import "net/http"
import "text/template"

func server(w http.ResponseWriter, r *http.Request) {
	error := r.URL.Query().Get("param")
	tmpl := template.New("error")
	tmpl, _ = tmpl.Parse(`{{define "T"}}{{.}}{{end}}`)
	tmpl.ExecuteTemplate(w, "T", error)
}

func main() {
	http.HandleFunc("/", server)
	http.ListenAndServe(":5000", nil)
}

By sending a request with param=<script>alert(1)</script>, the Content-Type of the response is set to text/html, resulting in an exposure to Cross-Site Scripting.

xss3.png

Prevention

Replace the text/template import with html/template, which already includes built-in Output Encoding capabilities.

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

func server(w http.ResponseWriter, r *http.Request) {
    error: = r.URL.Query().Get("param")
    tmpl: = template.New("error")
    tmpl, _ = tmpl.Parse(`{{define "T"}}{{.}}{{end}}`)
    tmpl.ExecuteTemplate(w, "T", error)
}

func main() {
    http.HandleFunc("/", server)
    http.ListenAndServe(":5000", nil)
}

By sending a request with error=<script>alert(1)</script>, the payload is correctly encoded, and thus harmless.

fixed.png

References

Checkmarx - Go Secure Coding Practices OWASP - Cross-Site Scripting (XSS) OWASP - Cross-Site Scripting Prevention Cheat Sheet