XPath 注入¶
ID: java/xml/xpath-injection
Kind: path-problem
Security severity: 9.8
Severity: error
Precision: high
Tags:
- security
- external/cwe/cwe-643
Query suites:
- java-code-scanning.qls
- java-security-extended.qls
- java-security-and-quality.qls
如果使用字符串串联构建 XPath 表达式,并且串联的组件包含用户输入,则用户很容易创建恶意的 XPath 表达式。
建议¶
如果用户输入必须包含在 XPath 表达式中,则应清理数据或预编译查询并使用变量引用来包含用户输入。
还可以通过使用 XQuery 来防止 XPath 注入。
示例¶
在最初三个示例中,代码接受用户指定的名称和密码,并在 XPath 表达式中使用此未验证且未清理的值。这容易受到用户提供特殊字符或字符串序列的影响,这些字符或字符串序列会更改 XPath 表达式的含义以搜索不同的值。
在第四个示例中,代码使用 setXPathVariableResolver
,它可以防止 XPath 注入。
最后两个示例用于 dom4j。它们展示了 XPath 注入的示例以及一种防止它的方法。
final String xmlStr = "<users>" +
" <user name=\"aaa\" pass=\"pass1\"></user>" +
" <user name=\"bbb\" pass=\"pass2\"></user>" +
"</users>";
try {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
//Document doc = builder.parse("user.xml");
Document doc = builder.parse(new InputSource(new StringReader(xmlStr)));
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
// Injectable data
String user = request.getParameter("user");
String pass = request.getParameter("pass");
if (user != null && pass != null) {
boolean isExist = false;
// Bad expression
String expression1 = "/users/user[@name='" + user + "' and @pass='" + pass + "']";
isExist = (boolean)xpath.evaluate(expression1, doc, XPathConstants.BOOLEAN);
System.out.println(isExist);
// Bad expression
XPathExpression expression2 = xpath.compile("/users/user[@name='" + user + "' and @pass='" + pass + "']");
isExist = (boolean)expression2.evaluate(doc, XPathConstants.BOOLEAN);
System.out.println(isExist);
// Bad expression
StringBuffer sb = new StringBuffer("/users/user[@name=");
sb.append(user);
sb.append("' and @pass='");
sb.append(pass);
sb.append("']");
String query = sb.toString();
XPathExpression expression3 = xpath.compile(query);
isExist = (boolean)expression3.evaluate(doc, XPathConstants.BOOLEAN);
System.out.println(isExist);
// Good expression
String expression4 = "/users/user[@name=$user and @pass=$pass]";
xpath.setXPathVariableResolver(v -> {
switch (v.getLocalPart()) {
case "user":
return user;
case "pass":
return pass;
default:
throw new IllegalArgumentException();
}
});
isExist = (boolean)xpath.evaluate(expression4, doc, XPathConstants.BOOLEAN);
System.out.println(isExist);
// Bad Dom4j
org.dom4j.io.SAXReader reader = new org.dom4j.io.SAXReader();
org.dom4j.Document document = reader.read(new InputSource(new StringReader(xmlStr)));
isExist = document.selectSingleNode("/users/user[@name='" + user + "' and @pass='" + pass + "']") != null;
// or document.selectNodes
System.out.println(isExist);
// Good Dom4j
org.jaxen.SimpleVariableContext svc = new org.jaxen.SimpleVariableContext();
svc.setVariableValue("user", user);
svc.setVariableValue("pass", pass);
String xpathString = "/users/user[@name=$user and @pass=$pass]";
org.dom4j.XPath safeXPath = document.createXPath(xpathString);
safeXPath.setVariableContext(svc);
isExist = safeXPath.selectSingleNode(document) != null;
System.out.println(isExist);
}
} catch (ParserConfigurationException e) {
} catch (SAXException e) {
} catch (XPathExpressionException e) {
} catch (org.dom4j.DocumentException e) {
}
参考资料¶
OWASP:测试 XPath 注入.
OWASP:XPath 注入.
通用弱点枚举:CWE-643.