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 , (see VOID IDS07-J. Prevent SQL Injection) wherein an attacker can enter valid SQL constructs into in 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 void Filter data that passes through a trust boundary.
...
If an attacker knows that Utah
is a valid login ID, they he or she can specify an input login ID such as:
...
Code Block |
---|
//users/user[login/text()='Utah' or '1'='1' and password/text()='xxxx' ] |
Since Because the '1'='1'
is automatically true, the password is never validated. Consequently, the attacker is falsely logged in as user Utah
, without having to know the password.
In order to To comply with guideline MSC05-J. Store passwords using a hash function, the passwords would have to be encrypted. Unfortunately, on many small systems, they are not, and so the password text added in the query string would match precisely what the user enters. An attacker could supply a password such as:
...
In this noncompliant code example, a user name and password is are 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 MSC05-J. Store passwords using a hash function and MSC56-J. Limit the lifetime of sensitive data.
If passed the attack string for login
described abovepreviously, the evaluate()
method call returns the corresponding login node in the XML file, causing the login()
method to return true
and bypass any authorization.
Code Block | ||
---|---|---|
| ||
private boolean doLogin(String loginID, char[] 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()='" + pwd + "' ]"); 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++) { Node node = nodes.item(i).getChildNodes().item(1).getChildNodes().item(0); System.out.println( "Authenticated: " + node.getNodeValue()); } return (nodes.getLength() >= 1); } |
...
- Treat all user input as untrusted and perform appropriate sanitization.
- When 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 sanitization. See guideline void Filter data that passes through a trust boundary 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 an analogous interface for XPath queries. 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.
Input File: login.qry
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 | ||
---|---|---|
| ||
private boolean doLogin(String loginID, String pwd)
throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse("users.xml");
XQuery xquery = new XQueryFactory().createXQuery(new File("login.xry"));
Map queryVars = new HashMap();
queryVars.put("loginid", loginID);
queryVars.put("password", pwd);
NodeList nodes = xquery.execute(doc, null, queryVars).toNodes();
// Print first names to the console
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i).getChildNodes().item(1).getChildNodes().item(0);
System.out.println( node.getNodeValue());
}
return (nodes.getLength() >= 1);
}
|
Using this method, the data specified in the loginID
and password
fields cannot be interpreted as executable content at runtime.
Wiki Markup |
---|
In addition, according to OWASP \[[OWASP 2005|AA. References#OWASP 05]\] recommends, |
Wiki Markup \[Prevention of XPath injection\] requires the following characters to be removed (ie, prohibited) or properly escaped:
< > / ' = "
to prevent straight parameter injection- XPath queries should not contain any meta characters (such as
' = * ? //
or similar)that you comprehensively test the existence of the file, and ensure that the files are within the bounds set by the Java 2 Security
Wiki Markup XSLT expansions should not contain any user input, or if they do,
\[you should] comprehensively test the existence of the file, and ensure that the files are within the bounds set by the Java 2 Security Policy.
Risk Assessment
Failure to validate user input may result in information disclosure and execution of unprivileged code.
...