...
Consider the following XML schema.
Code Block |
---|
<users> <user> <login>Utah<<username>Utah</login>username> <password>C^f3</password> </user> <user> <login>Bohdi<<username>Bohdi</login>username> <password>C@fe</password> </user> <user> <login>Busey<<username>Busey</login>username> <password>cAf3</password> </user> </users> |
Untrusted code may attempt to retrieve user details from this file with an XPath statement constructed dynamically from user input.
Code Block |
---|
//users/user[loginusername/text()='&LOGIN&' and password/text()='&PASSWORD&' ] |
If an attacker knows that Utah
is a valid login IDuser name, they can specify an input login ID such as:
Code Block |
---|
Utah' or '1'='1 |
This would yield the following query string:
Code Block |
---|
//users/user[loginusername/text()='Utah' or '1'='1' and password/text()='xxxx' ] |
Because the '1'='1'
is automatically true, the password is never validated. Consequently, the attacker is inappropriately logged in authenticated as user Utah
without having to know the password.
...
This would yield the following query string:
Code Block |
---|
//users/user[loginusername/text()='xxxx' and password/text()='' or '1'='1' ] |
This time, the '1'='1'
tautology disables both login ID name and password validation, and the attacker is inappropriately logged in authenticated without knowledge of either a login ID user name or a password.
Noncompliant Code Example
...
This example is vulnerable to the attack described above. If it is passed the attack string for login
username
described previously, the evaluate()
method call returns the corresponding login node in the XML file. This causes the logindoLogin()
method to return true
and bypass any authorization.
Code Block | ||
---|---|---|
| ||
private boolean doLogin(String loginIDuserName, 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[loginusername/text()='" + loginIDuserName + "' 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); } |
...
Code Block |
---|
declare variable $loginID$userName as xs:string external; declare variable $password as xs:string external; //users/user[@loginID@userName=$loginID$userName and @password=$password] |
This compliant solution uses a query specified in a text file by reading the file in the required format and then inserting values for the user name and password in a Map
. The XQuery
library constructs the XML query from these inputs.
Code Block | ||
---|---|---|
| ||
private boolean doLogin(String loginIDuserName, 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("loginiduserName", loginIDuserName); 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 userName
and password
fields cannot be interpreted as executable content at runtime.
...