Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: s/login/userName/g

...

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
bgColor#FFcccc
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
bgColor#ccccff
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.

...