Vulnerability analysis of Golang applications and more with Red Hat CodeReady Dependency Analytics v0.3.2

Have you ever encountered a pop-up when visiting a web page or browsing a particular item on a site? Imagine if these pop-ups were carriers that delivered malicious payloads to your devices or captured confidential information. This is a type of cyber attack called cross-site scripting, or XSS. Cross-site scripting is one of the most common attacks in 2022, and it made the OWASP top 10 web application security risks. Let's take a tour of cross-site scripting and learn how an attacker executes malicious JavaScript code on input parameters, creates pop-ups to deface web applications, and can hijack an active user session.

How JavaScript is used in XSS attacks

A dynamic web application is set up with three key features:

  1. HTML specifies the complete structure.
  2. CSS configures the overall look and feel.
  3. JavaScript adds powerful interactions to the application, such as warning popups, rollover effects, drop-down menus, and more.

JavaScript is the most popular scripting language. 95% of all websites are built and run via JavaScript. It implements interesting and powerful interactive features, and acts according to the user's actions.

Actions associated with executing JavaScript code include:

  • onclick: Executes JavaScript when the user clicks on a box or link.
  • onload: Executes JavaScript after a web page or image is completely loaded. An example:
    <body onload=alert('Welcome to Red Hat')>
  • onmouseover: Triggers JavaScript when the mouse passes over a URL link. An example:
    <a onmouseover=alert("redhat.com")>Contribute</a>
  • onmouseout: Triggers JavaScript if the mouse moves out of the window without clicking a URL.
  • onunload: Triggers JavaScript when the user leaves the web page by closing the browser or clicking a link.

XSS is a client-side code injection attack. In this kind of attack, websites are injected with malicious JavaScript code. XSS occurs when input parameters have not been correctly handled or validated in web applications, which allows an attacker to send malicious JavaScript code to a different end user. The end user's browser does not recognize it as a malicious script and falls into the XSS trap. This type of attack does not threaten users directly with a payload, but the attacker targets the XSS vulnerability by injecting a malicious script on a web page that seems to be a real part of the website. Thus, when any user visits this website, the XSS-afflicted website sends malicious JavaScript code to the user's browser without their knowledge.

Types of XSS attacks

There are a variety of types of XSS attacks, divided into three major categories:

  • Stored XSS, also known as permanent or type I XSS. In stored attacks, target servers store the injected script forever. The scripts maybe be stored in a database, on servers, or in forum comments. The victim unwittingly downloads that stored script from the server when attempting to access these resources.
  • Reflected XSS, also known as type-II or non-persistent XSS. These attacks happen when a web application instantly acknowledges the user's input without inspecting what has been entered. The attacker sends a malicious link via phishing to trick the user, and not store it on the webserver. There are two major types of reflected XSS: reflected XSS GET and reflected XSS POST.
  • DOM-based XSS: DOM-based XSS is a well-known vulnerability that happens in a document object model (DOM). The DOM defines web page segments such as the title, heading, table, forms, or a well-structured HTML page. If HTML documents are loaded into a web browser, they are transformed into a document object.

3 defense strategies for XSS attacks

Preventing cross-site scripting (XSS) attacks can be complicated, but these basic defense strategies are effective.

Have a content security policy

A content security policy (CSP) is a browser feature that allows you to create source lists for client-side web application resources, including JavaScript, CSS, and images. The CSP directs the browser to execute or display resources from specific sources using a special HTTP header.

In this example, the server only allows access to documents that are loaded over HTTPS via the single origin developers.redhat.com:


Content-Security-Policy: default-src https://developers.redhat.com

Deploy the X-XSS-Protection header

The XSS filter in recent web browsers is enabled by the X-XSS-Protection HTTP response header. Because the header is normally enabled by default, its function is to re-enable the filter for a specific website if the user has disabled it.

Make use of modern development frameworks

Modern JavaScript frameworks like AngularJS and ReactJS, along with server-side templating systems like Go Templates, offer good protection against reflected cross-site scripting.

Vulnerable code examples

In the previous sections, we examined JavaScript and its vulnerabilities. But a secure backend system like Go's HTTP Package can block misleading JavaScript functionality. Let's use this package to illustrate some analytic techniques.

In our first example, the server() method reads the parameter XYZ from the query string and returns it in the HTTP response. This method also handles HTTP GET requests. The http.DetectContentType function determines the default content-type response header.


package main
import "io"
import "net/http"

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

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

The content-type is set to text/plain when delivering a payload with XYZ=OpenSource, as you can see using a browser's developer tool (see Figure 1). This is not dangerous and is presented by the browser as plain text.

Firefox developer tool
Figure 1: Firefox developer tool shows the network header request.
Figure 1: Firefox's developer tool shows the network header request.

When I submit a request using XYZ=<script>alert("RedHat")</script>, the response's Content-Type is set to text/html, exposing the user to a cross-site scripting attack (see Figure 2).

Firefox developer tool
Figure 2: Executing our malicious JavaScript code processed by the backend.

This JavaScript delivery can do harmful things, and we must fix it. Fortunately, changing a simple package in Go can prevent it.

The example vulnerability can be minimized by encoding the user-controlled parameter's output. Several output encoding routines are included in the html/template package in Go. The problem in this example can be solved by using the HTMLEscapeString method to perform output encoding on the user-supplied input:


import "io"
import "net/http"

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

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

Now let's consider a second example. The server() function reads the parameter error from the query string and adds it to the template (text/template module) that handles HTTP GET requests. The http.DetectContentType function determines the default content-type response header.


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

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

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

When I submit a request using XYZ=<script>alert("RedHat")</script>, the response's content-type sets to text/html, exposing the user to cross-site scripting, as shown in Figure 3.

Firefox developer tool
Figure 3: Executing the malicious JavaScript code and successfully injecting our code.

To fix this vulnerable code, replace the text/template import with html/template with built-in output encoding capabilities:


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

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

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

When a request with XYZ=<script>alert("RedHat")</script> is sent, the updated code successfully encodes the payload (see Figure 4). The fixed backend code has successfully filtered the attack, preventing it from executing malicious JavaScript code.

Firefox developer tool
Figure 4: Executing malicious JavaScript code but backend blocked the request.
Figure 4: The backend blocks a request for malicious JavaScript code.

Conclusion

Cutting-edge programming languages like Go make it easy to fix security issues such as cross-site scripting, server-side request forgery, and comment injections. With secure coding practices and continuous security testing, you can prevent various cyber-attacks. Learn more about secure coding practice by reviewing the OWASP secure coding practice guide, or read Black Hat Go for information about security penetration testing.

Last updated: February 5, 2024