INJ03-G: Prevent XML Injection
Introduction
Extensible markup language (XML) is designed to help store, structure and transfer data. It is used in many applications because of its platform independence, flexibility, and relative simplicity. However, its versatility makes XML vulnerable to a wide spectrum of attacks, including XML injection. XML or SOAP injection vulnerabilities arise when user input is inserted into a server-side XML document or SOAP message unsafely. It may be possible to use XML metacharacters to modify the structure of the resulting XML. Depending on the function in which the XML is used, it may be possible to interfere with the application's logic, to perform unauthorized actions or access sensitive data.
XML tags can be injected into an XML document when input string data is incorporated into it. When the XML parser interprets these tags, they can cause data to be overridden.
For example, an online store application that allows the user to specify the quantity of an item available for purchase might generate the following XML document:
<item>
<description>Widget</description>
<price>500.0</price>
<quantity>1</quantity>
</item>
An attacker might input the following string instead of a count for the quantity:
1</quantity><price>1.0</price><quantity>1
Resulting in a modified XML:
<item>
<description>Widget</description>
<price>500.0</price>
<quantity>1</quantity><price>1.0</price><quantity>1</quantity>
</item>
In this example, an XML parser may interpret the XML so that the second price field supersedes the first, potentially altering the item's price to $1. Alternatively, attackers may inject special characters, such as comment blocks and character data (CDATA) delimiters, which corrupt the meaning of the XML.
Non-Compliant Code Example
In the following non-compliant code example, a client method uses simple string concatenation to build an XML query to send to a server. XML injection is possible because the method performs no input validation.
// Non-Compliant Code Example
static function createXMLStreamBad(final outStream : BufferedOutputStream,
final quantity : String) : void {
var xmlString : String =
"<item>\n<description>Widget</description>\n” +
"<price>500</price>\n<quantity>" + quantity +
"</quantity></item>"
outStream.write(xmlString.getBytes(StandardCharsets.UTF_8))
outStream.flush()
}
Compliant Solution (Input Validation)
The application should validate or sanitize user input before incorporating it into an XML document or SOAP message. It may be possible to block any input containing XML metacharacters such as <
and >
. Alternatively, these characters can be replaced with the corresponding entities: <
and >
.
Depending on the specific data and command interpreter or parser to which data is being sent, appropriate methods must be used to sanitize untrusted user input. The following compliant solution validates that quantity is an unsigned integer:
// Compliant Solution
static function createXMLStream(final outStream : BufferedOutputStream,
final quantity : String) : void {
var count : int = Integer.parseUnsignedInt(quantity)
var xmlString : String =
"<item>\n<description>Widget</description>\n” +
"<price>500</price>\n<quantity>" + count +
"</quantity></item>"
outStream.write(xmlString.getBytes(StandardCharsets.UTF_8))
outStream.flush()
}
The sanitization approach used in this compliant solution can reject valid directories. Also, because the command interpreter invoked is system-dependent, proving that this solution prevents command injections on every platform a Gosu program might run is difficult.
Compliant Solution (XML Schema)
A more general mechanism for checking XML for attempted injection is to validate it using a Document Type Definition (DTD) or schema. The schema must be rigidly defined to prevent injections from being mistaken for valid XML. Here is a suitable schema for validating the XML snippet:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="order">
<xs:complexType>
<xs:sequence>
<xs:element name="item" maxOccurs="10" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="description" type="xs:string" />
<xs:element name="price" type="xs:decimal" />
<xs:element name="quantity" type="xs:nonNegativeInteger" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
The schema is available as the file schema.xsd
. This compliant solution employs this schema to prevent XML injection from succeeding. It also relies on the CustomResolver class defined in INJ04-G: Prevent XML External Entity Attacks s to prevent XML external entity (XXE) attacks.
// Compliant Solution
static function createXMLStreamDTD(final outStream : BufferedOutputStream,
final quantity : String) : void {
var xmlString : String =
"<order><item>\n<description>Widget</description>\n” +
"<price>500.0</price>\n<quantity>" + quantity +
"</quantity></item></order>"
var xmlStream = new InputSource(new StringReader(xmlString))
var sf : SchemaFactory = SchemaFactory.newInstance(
XMLConstants.W3C_XML_SCHEMA_NS_URI
)
var defHandler = new DefaultHandler() {
override public function warning(s: SAXParseException): void { throw s }
override public function error(s : SAXParseException) : void { throw s }
override public function fatalError(s: SAXParseException) : void {
throw s
}
}
var ss = new StreamSource(new File("inj03_g//schema.xsd"))
try {
var schema : Schema = sf.newSchema(ss)
var spf : SAXParserFactory = SAXParserFactory.newInstance()
spf.setSchema(schema)
var saxParser : SAXParser = spf.newSAXParser()
var reader : XMLReader = saxParser.getXMLReader()
reader.setEntityResolver(new CustomResolver())
saxParser.parse(xmlStream, defHandler)
} catch (x : ParserConfigurationException) {
throw new IOException("Unable to validate XML", x)
} catch (x : SAXException) {
throw new IOException("Invalid quantity", x)
}
outStream.write(xmlString.getBytes(StandardCharsets.UTF_8))
outStream.flush()
}
Using a schema or DTD to validate XML is convenient when receiving XML that may have been loaded with unsanitized input. If such an XML string has not yet been built, sanitizing input before constructing XML yields better performance.
Risk Assessment
Failure to sanitize user input before processing or storing it can result in injection attacks.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
ERR03-G | Medium | Likely | High | L4 | L3 |
Additional resources
- Gosu Secure Coding Guidelines – INJ04-G Prevent XML External Entity Attacks
- SEI CERT Oracle Coding Standard for Java
- ISO/IEC TR 24772:2019 - Injection [RST]
- MITRE CWE
- XML Security Cheat Sheet
- Testing for XML Injection
- SQL Injection Prevention Cheat Sheet
- OWASP Top Ten 2017
- Secure Coding Rules for Java, Part I
- Java Platform, Standard Edition: Security Developer’s Guide
- Permissions in the JDK
- Extensible Markup Language (XML) 1.0 (Fifth Edition)
Was this page helpful?