Skip to main content

XML injection (XXE)

Need

Prevention of XML injection attacks

Context

  • Usage of Go 1.16 for building high-performance and scalable applications
  • Usage of Gin framework for building web applications in Go
  • Usage of encoding/xml for parsing and encoding XML data
  • Usage of net/http for creating HTTP servers in a Node.js application

Description

Non compliant code

package main

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

type User struct {
FirstName string `xml:"firstname"`
LastName string `xml:"lastname"`
}

func main() {
router := gin.Default()

router.POST("/xml", func(c *gin.Context) {
var user User
if err := c.ShouldBindXML(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"firstname": user.FirstName, "lastname": user.LastName})
})

router.Run()
}

In the above code, the application is using the Gin web framework to handle HTTP requests. In the POST "/xml" route, it's expecting an XML payload which it binds to a User struct using the ShouldBindXML method.

This code is vulnerable to XML External Entity (XXE) Injection attacks because it does not disable the use of external entities when parsing the XML data. If an attacker sends an XML payload that includes an external entity, the server will process it.

For example, an attacker could send a payload like this:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [ <!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<foo>&xxe;</foo>

This payload defines an external entity &xxe; that references a local file on the server (/etc/passwd). When the server processes this payload, it will replace &xxe; with the content of the /etc/passwd file, potentially revealing sensitive information to the attacker.

To fix this vulnerability, you need to disable the use of external entities in your XML parser.

Steps

  • Validate and sanitize user input: Before processing any XML data, ensure that it is properly validated and sanitized to prevent any malicious XML injection. Use input validation techniques such as whitelisting or regular expressions to ensure that only expected data is accepted.
  • Disable external entity resolution: Disable the ability to resolve external entities in XML parsing. This can be done by setting the appropriate options or configurations in the XML parser library being used.
  • Use a secure XML parser: Make sure to use a secure XML parser library that is not vulnerable to XML injection attacks. Choose a parser that has built-in protections against XXE vulnerabilities.
  • Implement strict parsing: Configure the XML parser to use strict parsing mode, which enforces stricter rules for parsing XML documents. This can help prevent the interpretation of potentially malicious XML code.
  • Limit XML entity expansion: Set limits on the number of entity expansions allowed during XML parsing. This can help prevent denial-of-service attacks that exploit entity expansion vulnerabilities.
  • Implement proper error handling: Handle XML parsing errors properly to prevent information leakage. Avoid displaying detailed error messages to users, as they can provide valuable information to attackers.
  • Regularly update dependencies: Keep all dependencies, including the XML parser library, up to date with the latest security patches and updates. This helps ensure that any known vulnerabilities are addressed.
  • Perform security testing: Regularly conduct security testing, including penetration testing and code reviews, to identify and address any potential vulnerabilities in the application's XML processing functionality.

Compliant code

package main

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

type User struct {
FirstName string `xml:"firstname"`
LastName string `xml:"lastname"`
}

func main() {
router := gin.Default()

router.POST("/xml", func(c *gin.Context) {
var user User
if err := c.ShouldBindXML(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

// Validate user input
if !isValid(user.FirstName) || !isValid(user.LastName) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"})
return
}

c.JSON(http.StatusOK, gin.H{"firstname": user.FirstName, "lastname": user.LastName})
})

router.Run()
}

// isValid is a function to validate user input
func isValid(input string) bool {
// Implement your validation logic here
// For example, you can check if the input contains only alphanumeric characters
return true
}

The original code was vulnerable to XML External Entity (XXE) Injection attacks. This is because it was parsing XML input from users without validating or sanitizing it first. An attacker could exploit this vulnerability to inject malicious XML code into the application's requests, which could then be interpreted by the server.

The fixed code includes a validation function isValid that checks the user input before it is processed. This function should be implemented with the appropriate validation logic to ensure that only expected data is accepted. For example, it could check if the input contains only alphanumeric characters.

In addition to validating user input, it's also important to disable the ability to resolve external entities in XML parsing, use a secure XML parser, implement strict parsing, limit XML entity expansion, handle XML parsing errors properly, regularly update dependencies, and perform regular security testing. However, these measures are not shown in the code as they depend on the specific XML parser library being used and the overall application architecture.

References