XPath injection occurs when an XML document is used for data storage in a manner similar to a relational database. This attack is similar to SQL injection, (guideline see IDS07-J. Prevent SQL Injection) wherein an attacker can enter valid SQL constructs into the data fields of the query in use. Typically, the conditional field of the query resolves to a tautology or gives the attacker access to privileged information. This guideline is a specific example of the broadly scoped guideline IDS00-J. Always validate user input.
XML Path Injection Example
Consider the following XML schema.
Code Block |
---|
<users> <user> <login>Utah</login> <password>C^f3</password> </user> <user> <login>Bohdi</login> <password>C@fe</password> </user> <user> <login>Busey</login> <password>cAf3</password> </user> </users> |
Note that the passwords must first be encrypted through a hashing function, so that they are not stored in the database in cleartext. See MSC18-J Store passwords using a hash function for more information.
Untrusted code may attempt to retrieve user details from this file with an XPath statement constructed dynamically from user input.
Code Block |
---|
str_query = "//users/user[LoginID/text()= "& login & " '&LOGIN&' and password/text()="& password &"]"'&PASSWORD&' ] |
An attacker may specify input such as, login = ' or :
Code Block |
---|
' or 1=1 |
for both the login and password fields. This would yield and password = ' or 1=1
, yielding the following query string.:
Code Block |
---|
//users/user[LoginID/text()='' or 1=1 ' and password/text()='' or 1=1' ] |
This may expose all the records in the XML file
...
In this noncompliant code example, a user name and password is read from the user and used to construct the query string. The password is passed as a char array, and then hashed, all to comply with MSC18-J Store passwords using a hash function and MSC10-J. Limit the lifetime of sensitive data.
If passed the attack string for login
described above, the evaluate()
method evaluate function call returns a set of all nodes in the XML file, causing the login
function ()
method to return true
and bypass any authorization.
Code Block | ||
---|---|---|
| ||
class XpathInjection { private boolean doLogin(String loginID, Stringchar[] password) throws ParserConfigurationException, SAXException,IOException, XPathExpressionException { DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); domFactory.setNamespaceAware(true); DocumentBuilder builder = domFactory.newDocumentBuilder(); Document doc = builder.parse("users.xml"); String pwd = hashPassword( password); XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); XPathExpression expr = xpath.compile("//users/user[login/text()='" + loginID + "'" + "and password/text()='" + passwordpwd + "' ]"); Object result = expr.evaluate(doc, XPathConstants.NODESET); NodeList nodes = (NodeList) result; // Print first names to the console for (int i = 0; i < nodes.getLength(); i++) { System.out.println(nodes.item(i).getNodeValue());} return (nodes.getLength() >= 1); } } |
...
- Treat all user input as untrusted and perform appropriate sanitization.
- When validating sanitizing user input, verify the correctness of the data type, length, format and the content. For example, use a regular expression that checks for XML tags and special characters in user input. This corresponds to input validationsanitization. See guideline IDS00-J. Always validate user input for additional details.
- In a client-server application, perform validation at both the client and the server side.
- Extensively test applications that supply, propagate, or use user input.
An effective prevention technique for preventing the related issue of SQL injection is parameterization, whereby user-specified data is passed to an API as a parameter, thus ensuring that user-specified data is never interpreted as executable logic. Unfortunately, Java SE currently lacks such an analogous interface for XPath queries. Emulate SQL parameterization can be emulated by using an interface (such as XQuery
) that supports specifying a query statement in a separate file that is supplied at runtime. This compliant solution uses a query specified in a text file by reading the file in the required format and then entering values for the user name and password in a Map
. The XQuery
library constructs the XML query from these elements.
...
Code Block |
---|
declare variable $loginID as xs:string external; declare variable $password as xs:string external; //users/user[@loginID= $loginID and @password=$password] |
Code Block | ||
---|---|---|
| ||
Document doc = new Builder().build("users.xml"); XQuery xquery = new XQueryFactory().createXQuery(new File("login.xry")); Map queryVars = new HashMap(); queryVars.put("loginid", "Utah"loginID); // User name hardcoded for// illustrativeeg purposes"Utah" queryVars.put("password", "securecoding"pwd); // Password hardcoded for illustrative purposes // eg "C^f3" Nodes results = xquery.execute(doc, null, queryVars).toNodes(); for (int i = 0; i < results.size(); i++) { System.out.println(results.get(i).toXML()); } |
Using this method, the data specified in the loginID
and password
fields cannot be interpreted as executable content at runtime.
...