《Java编码指南:编写安全可靠程序的75条建议》—— 指南8:防止XPath注入

简介: 可扩展标记语言(Extensible Markup Language,XML)可用于以类似于关系数据库的方式来存储数据。XML文档中的数据通常是用XPath来检索。当给XPath检索例程提供的数据没有做合适的无害化处理时,就有可能会导致XPath注入(XPath injection)攻击。

本节书摘来异步社区《Java编码指南:编写安全可靠程序的75条建议》一书中的第1章,第1.8节,作者:【美】Fred Long(弗雷德•朗), Dhruv Mohindra(德鲁•莫欣达), Robert C.Seacord(罗伯特 C.西科德), Dean F.Sutherland(迪恩 F.萨瑟兰), David Svoboda(大卫•斯沃博达),更多章节内容可以访问云栖社区“异步社区”公众号查看。

指南8:防止XPath注入

可扩展标记语言(Extensible Markup Language,XML)可用于以类似于关系数据库的方式来存储数据。XML文档中的数据通常是用XPath来检索。当给XPath检索例程提供的数据没有做合适的无害化处理时,就有可能会导致XPath注入(XPath injection)攻击。这种攻击类似于SQL注入或XML注入(参见《The CERT® Oracle® Secure Coding Standard for Java™》[Long 2012]的“IDS00-J. Sanitize untrusted data passed across a trust boundary”。攻击者可以在查询用的数据字段中输入有效的SQL构造或XML构造。典型的攻击是,让条件查询字段解析为一个永真式,这样就会导致攻击者访问到未经授权的信息。

该指南是指南7的一个具体示例。

XML路径注入示例
先来看看下面的XML模式。

<users>
 <user>
  <username>Utah</username>
  <password>e90205372a3b89e2</password>
 </user>
 <user>
  <username>Bohdi</username>
  <password>6c16b22029df4ec6</password>
 </user>
 <user>
  <username>Busey</username>
  <password>ad39b3c2a4dabc98</password>
 </user>
</users>```
密码已被散列加密,这符合指南13。出于演示目的,这里的密码就用MD5散列算法加密;在实践中,应该使用SHA-256这样的更安全的算法。

不可信的代码可能会尝试在用户输入中动态构造XPath语句,然后利用这个语句从XML文件中检索出用户的详细信息。

//users/user[username/text()='&LOGIN&' and
 password/text()='&PASSWORD&' ]`
如果攻击者知道Utah是一个有效的用户名,他可以指定一个下面这样的输入:

Utah' or '1'='1````
这样就构造出以下查询字符串:

//users/user[username/text()='Utah' or '1'='1'
 and password/text()='xxxx']`
因为'1'='1'自动为真,所以密码永远也不会被检查。因此,攻击者在不知道用户Utah的密码的情况下被不适当地验证成了该用户。

违规代码示例

下面的违规代码示例从用户输入中读取用户名和密码,并使用它们来构建查询字符串,将密码以字符数组的形式传递,然后对其进行散列加密。这个示例容易受到上面提到的那种方式的攻击。如果将上面描述的攻击字符串传递给evaluate()方法,这个方法调用会返回XML文件中的相应节点,这会导致doLogin()方法返回true,并绕过所有授权。

private boolean doLogin(String userName, 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[username/text()='" +
   userName + "' 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);
}```
合规解决方案(XQuery)
为了防止XPath注入,可以采用类似于防止SQL注入的方式。

将所有的用户输入视为不可信,并执行适当的无害化处理。
对用户输入进行无害化处理时,验证数据类型、数据长度、数据格式和数据内容的正确性。例如,使用一个正则表达式检查用户输入中是否包含XML标签和特殊字符。这种做法符合对用户输入进行无害化处理的规范。更多细节参见指南7。
在客户端-服务器(client-server,CS)应用程序中,既执行客户端验证,也执行服务器端验证。
广泛地测试用于提供、传播或接受用户输入的应用程序。
一种有效防止SQL注入相关问题的技术是参数化。参数化能确保将用户指定的数据以参数的形式传递给API,这样数据就不会被解释为可执行内容了。遗憾的是,Java SE目前缺乏一个类似于XPath查询的接口。不过,通过使用XQuery这样的接口,XPath可以模拟SQL参数化。XQuery支持将查询语句写入运行时环境中的一个单独文件中。

输入文件:login.xq
declare variable $userName as xs:string external;
declare variable $password as xs:string external;
//users/user[@userName=$userName and @password=$password]`
下面的合规解决方案从一个文本文件中读取所需的特定格式的查询语句,然后将用户名和密码的值插入一个映射中。XQuery库构造了这些来自用户输入的XML查询。

private boolean doLogin(String userName, 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.xq"));
 Map queryVars = new HashMap();
 queryVars.put("userName", userName);
 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);
}```
使用这种方法,用户名(userName)和密码(password)字段中输入的数据不会被运行时环境解释为可执行的内容。

适用性
未能验证用户输入可能会导致信息披露和未经授权代码的执行。

根据OWASP [OWASP 2013]:

(防止XPath注入)需要被删除(即禁止)或适当转义的字符如下。

``< > / ' = "``可用于防止直接的参数注入。
XPath查询不应该包含任何元字符(如'、=、*、?、//或类似字符)。
相关文章
|
1月前
|
Java
java程序导出堆文件
java程序导出堆文件
|
1月前
|
SQL Java 应用服务中间件
Java项目防止SQL注入的四种方案
Java项目防止SQL注入的四种方案
40 0
|
13天前
|
Java Maven
【Java报错】显示错误“Error:java: 程序包org.springframework.boot不存在“
【Java报错】显示错误“Error:java: 程序包org.springframework.boot不存在“
34 3
|
17天前
|
XML 前端开发 Java
《手把手教你》系列技巧篇(十四)-java+ selenium自动化测试-元素定位大法之By xpath上卷(详细教程)
【4月更文挑战第6天】按宏哥计划,本文继续介绍WebDriver关于元素定位大法,这篇介绍定位倒数二个方法:By xpath。xpath 的定位方法, 非常强大。使用这种方法几乎可以定位到页面上的任意元素。xpath 是XML Path的简称, 由于HTML文档本身就是一个标准的XML页面,所以我们可以使用Xpath 的用法来定位页面元素。XPath 是XML 和Path的缩写,主要用于xml文档中选择文档中节点。基于XML树状文档结构,XPath语言可以用在整棵树中寻找指定的节点。
43 0
|
1天前
|
Java
网页运行java程序cheerpj
网页运行java程序cheerpj
25 0
|
12天前
|
Java API
编码的奇迹:Java 21引入有序集合,数据结构再进化
编码的奇迹:Java 21引入有序集合,数据结构再进化
16 0
|
12天前
|
Java Shell
Java 21颠覆传统:未命名类与实例Main方法的编码变革
Java 21颠覆传统:未命名类与实例Main方法的编码变革
13 0
|
28天前
|
Java
elasticsearch使用java程序添加删除修改
elasticsearch使用java程序添加删除修改
9 0
|
1月前
|
Java
java程序
re是java运行时的环境,包含jvm和运行时所需要的类库 jdk是java开的程序包,包含jre和开发人员使用的工具 jvm就是我们常说的java虚拟机,他是整个java实现跨平台的最核心 的部分,所有的java程序会首先被编译为.class的类文件,这种类文 件可以在虚拟机上执行。也就是说class并不直接与机器的操作系统 相对应,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释 给本地系统执行。 只有jvm还不能成class的执行,因为再解释class的时候jvm需要调用 解释所需要的类库lib,而jre包含lib类库。jvm屏蔽了与具体操作系 统平台相关的信息,使得java程
17 0