《Java编码指南:编写安全可靠程序的75条建议》—— 指南8:防止XPath注入-阿里云开发者社区

开发者社区> 异步社区> 正文

《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查询不应该包含任何元字符(如'、=、*、?、//或类似字符)。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
《Java编码指南:编写安全可靠程序的75条建议(英文版)》—— 2.9 总结
与此同时,读者还学习了一些基本的计算机编程概念,如编译器、解释器、块、语句和变量。随着后续章节的学习,读者将会对这些概念越来越清晰。只要读者在本章成功地运行了Saluton程序,就可以进入下一章。
1241 0
DES安全编码组件
  DES安全编码组件   支持 DES、DESede(TripleDES,就是3DES)、AES、Blowfish、RC2、RC4(ARCFOUR)   DES           key size must be equal to 56   DESede(TripleDES) key size must be equal to 112 or 168   AES      
991 0
《Java编码指南:编写安全可靠程序的75条建议(英文版)》—— 2.11 测验
通过回答下列问题检测对本章介绍的知识的掌握程度。
1270 0
java项目编码格式转换(如GBK转UTF-8)
昨天突然下了个Java项目,把项目导入到eclipse中,发现项目是gbk编码格式想把项目变为utf-8,但是发现转换格式比较麻烦就写了这个代码,后面改进了下,想到说不定有人也需要就把它写了出来 代码如下 代码比较简单看懂了自己可以写一下,可以当做一个关于io流的一个练习 import java.
1743 0
利用Java编码测试CSRF令牌验证的Web API
前一篇拙文是利用了Jmeter来测试带有CSRF令牌验证的Web API;最近几天趁着项目不忙,练习了用编码的方式实现。 有了之前Jmeter脚本的基础,基本上难点也就在两个地方:获取CSRF令牌、Cookie的传递。
906 0
《Java编码指南:编写安全可靠程序的75条建议(英文版)》—— 2.12 练习
如果想更多地探索本章介绍的主题,可完成下列练习。 将英文短语“Hello world!”翻译成其他语言,翻译时可以使用谷歌翻译,编写一个程序,让计算机用法语、意大利语或葡萄牙语向世界问候。
1350 0
05.Java网络编程(代码实践)
计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路链接起来,在网络操作系统,网络管理软件及网络通信协议的协调下,实现资源贡献和信息传递的计算机系统 网络编程就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换 网...
788 0
+关注
异步社区
异步社区(www.epubit.com)是人民邮电出版社旗下IT专业图书旗舰社区,也是国内领先的IT专业图书社区,致力于优质学习内容的出版和分享,实现了纸书电子书的同步上架,于2015年8月上线运营。公众号【异步图书】,每日赠送异步新书。
12049
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载