Skip to main content

DOM-Based cross-site scripting (XSS)

Need

Prevention of cross-site scripting (XSS) attacks

Context

  • Usage of Go 1.16 for building efficient and scalable applications
  • Usage of gin-gonic/gin for building web applications in Go
  • Usage of net/http for handling HTTP requests and responses in a Node.js server

Description

Non compliant code

package main

import (
"github.com/gin-gonic/gin"
"net/http"
)

func main() {
r := gin.Default()
r.GET("/search", func(c *gin.Context) {
query := c.Query("query")
c.String(http.StatusOK, "Search Results: "+query)
})
r.Run()
}

This is a simple Go web application using the Gin framework. It has a single endpoint /search that accepts a GET request with a query parameter query.

In the handler function for this endpoint, it retrieves the query parameter from the request URL using c.Query("query"). This value is directly concatenated with the string "Search Results: " and sent back to the client as the HTTP response body using c.String(http.StatusOK, "Search Results: "+query).

The vulnerability lies in the fact that the application does not validate or sanitize the query parameter before using it to generate dynamic content. This means an attacker could potentially inject malicious scripts as part of the query parameter, leading to DOM-based Cross-Site Scripting (XSS) attacks.

For example, an attacker could send a request like GET /search?query=<script>malicious_code_here</script>. The server would then respond with "Search Results: ", and if this response is used as innerHTML in the client's DOM, the malicious script would be executed.

This vulnerability can lead to various security issues like session hijacking, identity theft, defacement of the website, and other malicious activities.

Steps

  • Implement input validation and sanitization to ensure that user input is safe and does not contain any malicious code.
  • Use context-aware output encoding to properly encode user-generated content before displaying it in the response.
  • Avoid using client-side rendering for dynamic content generation. Instead, generate the content on the server-side and send it to the client as static HTML.
  • Implement a Content Security Policy (CSP) to restrict the types of content that can be loaded and executed on the web page.
  • Regularly update and patch the web application framework and its dependencies to ensure that any known vulnerabilities are addressed.

Compliant code

package main

import (
"github.com/gin-gonic/gin"
"github.com/microcosm-cc/bluemonday"
"net/http"
)

func main() {
r := gin.Default()
r.GET("/search", func(c *gin.Context) {
query := c.Query("query")
p := bluemonday.UGCPolicy()
safeQuery := p.Sanitize(query)
c.String(http.StatusOK, "Search Results: "+safeQuery)
})
r.Run()
}

The original code was vulnerable to DOM-Based Cross-Site Scripting (XSS) attacks because it directly used the user input in the response without any validation or sanitization. This could allow an attacker to inject malicious scripts into the web page, which would then be executed in the user's browser.

The fixed code uses the bluemonday library to sanitize the user input before using it in the response. The bluemonday.UGCPolicy() function returns a policy that allows a common set of safe HTML elements and attributes that are typically found in user generated content. This policy is then used to sanitize the user input, effectively removing any potentially harmful scripts.

In addition to this, it is recommended to implement a Content Security Policy (CSP) to restrict the types of content that can be loaded and executed on the web page. This can further protect against XSS attacks by preventing the execution of unauthorized scripts.

Finally, it is important to regularly update and patch the web application framework and its dependencies to ensure that any known vulnerabilities are addressed.

References