首页 > 代码库 > XPATH 注入的介绍与代码防御

XPATH 注入的介绍与代码防御

0x01 介绍
软件未正确对 XML 中使用的特殊元素进行无害化处理,导致攻击者能够在终端系统处理 XML 的语法、内容或命令之前对其进行修改。在 XML 中,特殊元素可能包括保留字或字符,例如“<”、“>”、“"”和“&”,它们可能用于添加新数据或修改 XML 语法。我们发现用户可控制的输入并未由应用程序正确进行无害化处理,就在 XPath 查询中使用。例如,假定 XML 文档包含“user”名称的元素,每个元素各包含 3 个子元素 -“name”、“password”和 “account”。这时下列 XPath 表达式会产生名称为“jsmith”、密码为“Demo1234”的用户的帐号(如果没有这样的用户,便是空字符串):
string(//user[name/text()=‘jsmith‘ and password/text()=‘Demo1234‘]/account/text())
以下是应用程序的示例(采用 Microsoft ASP.NET 和 C#):
XmlDocument XmlDoc = new XmlDocument();
XmlDoc.Load("...");
...
XPathNavigator nav = XmlDoc.CreateNavigator();
XPathExpression expr = nav.Compile("string(//user[name/text()=‘"+TextBox1.Text+"‘ and password/text()=‘"+TextBox2.Text+"‘]/account/text())");
String account=Convert.ToString(nav.Evaluate(expr));
if (account=="")
{
// name+password pair is not found in the XML document - login failed.
}
else
{
// account found -> Login succeeded. Proceed into the application
}
使用此类代码时,攻击者可以注入 XPath 表达式(非常类似 SQL 注入),例如,提供以下值作为用户名:
‘ or 1=1 or ‘‘=‘
此数据会导致原始 XPath 的语义发生更改,使其始终返回 XML 文档中的第一个帐号。
这意味着虽然攻击者未提供任何有效用户名或密码,但仍将登录(作为 XML 文档中列出的第一个用户)。
0x02 主要修复思路
未对用户输入正确执行危险字符清理,可能导致恶意攻击者可以访问存储在敏感数据资源中的信息。缓解修复:
假设所有输入都是恶意的。使用黑名单和白名单的适当组合以确保系统仅处理有效和预期的输入。
0x03 Asp.Net
我们可以使用验证控件,将输入验证添加到“Web 表单”页面。验证控件提供适用于所有常见类型的标准验证的易用机制 - 例如,测试验证日期是否有效,或验证值是否在范围内 - 以及进行定制编写验证的方法。此外,验证控件还使我们能够完整定制向用户显示错误信息的方式。验证控件可搭配“Web 表单”页面的类文件中处理的任何控件使用,其中包括 HTML 和 Web 服务器控件。
为了确保用户输入仅包含有效值,我们可以使用以下其中一种验证控件:
a. “RangeValidator”:检查用户条目(值)是否在指定的上下界限之间。 您可以检查配对数字、字母字符和日期内的范围。
b. “RegularExpressionValidator”:检查条目是否与正则表达式定义的模式相匹配。 此类型的验证使您能够检查可预见的字符序列,如社会保险号码、电子邮件地址、电话号码、邮政编码等中的字符序列。
重要注意事项:验证控件不会阻止用户输入或更改页面处理流程,它们只会设置错误状态,并产生错误消息。程序员的职责是,在执行进一步的应用程序特定操作前,测试代码中控件的状态。有两种方法可检查用户输入的有效性:
1. 测试常规错误状态
在您的代码中,测试页面的 IsValid 属性。该属性会将页面上所有验证控件的 IsValid 属性值汇总(使用逻辑 AND)。如果将其中一个验证控件设置为无效,那么页面属性将会返回 false。
2. 测试个别控件的错误状态
在页面的“验证器”集合中循环,该集合包含对所有验证控件的引用。然后,您就可以检查每个验证控件的 IsValid 属性。
0x03 J2EE
1 输入数据验证
虽然为了用户的方便,可以提供“客户端”层的数据验证,但必须在“服务器”层(也就是 Servlet)执行数据验证。客户端验证本身就不安全,因为这些验证可轻易绕过,例如,通过禁用 JavaScript。一份好的设计通常需要 Web 应用程序框架,以提供服务器端实用程序例程,从而验证以下内容:[1] 必需字段[2] 字段数据类型(缺省情况下,所有 HTTP 请求参数都是“字符串”)[3] 字段长度[4] 字段范围[5] 字段选项[6] 字段模式[7] cookie 值[8] HTTP 响应好的做法是将以上例程作为“验证器”实用程序类中的静态方法实现。以下部分描述验证器类的一个示例。
[1] 必需字段“始终”检查字段不为空,并且其长度要大于零,不包括行距和后面的空格
以下是如何验证必需字段的示例:
<span style="font-size:14px;"> // Java example to validate required fields
public Class Validator {
...
public static boolean validateRequired(String value) {
boolean isFieldValid = false;
if (value != null && value.trim().length() > 0) {
isFieldValid = true;
}
return isFieldValid;
}
}
String fieldValue = http://www.mamicode.com/request.getParameter("fieldName");
if (Validator.validateRequired(fieldValue)) {
// fieldValue is valid, continue processing request
}
</span>
[2] 输入的 Web 应用程序中的字段数据类型和输入参数欠佳
所有 HTTP 请求参数或 cookie 值的类型都是“字符串”。开发者负责验证输入的数据类型是否正确。使用 Java 基本包装程序类,来检查是否可将字段值安全地转换为所需的基本数据类型。以下是验证数字字段(int 类型)的方式的示例:
<span style="font-size:14px;"> // Java example to validate that a field is an int number
public Class Validator {
...
public static boolean validateInt(String value) {
boolean isFieldValid = false;
try {
Integer.parseInt(value);
isFieldValid = true;
} catch (Exception e) {
isFieldValid = false;
}
return isFieldValid;
}
...
}
...
// check if the HTTP request parameter is of type int
String fieldValue = http://www.mamicode.com/request.getParameter("fieldName");
if (Validator.validateInt(fieldValue)) {
// fieldValue is valid, continue processing request
...
}
</span>
好的做法是将所有 HTTP 请求参数转换为其各自的数据类型。例如,开发者应将请求参数的“integerValue”存储在请求属性中,并按以下示例所示来使用:
<span style="font-size:14px;"> // Example to convert the HTTP request parameter to a primitive wrapper data type
// and store this value in a request attribute for further processing
String fieldValue = http://www.mamicode.com/request.getParameter("fieldName");
if (Validator.validateInt(fieldValue)) {
// convert fieldValue to an Integer
Integer integerValue = http://www.mamicode.com/Integer.getInteger(fieldValue);
// store integerValue in a request attribute
request.setAttribute("fieldName", integerValue);
}
// Use the request attribute for further processing
Integer integerValue = http://www.mamicode.com/(Integer)request.getAttribute("fieldName");
</span>
以下是应用程序应处理的主要 Java 数据类型(如上所述):
- Byte
- Short
- Integer
- Long
- Float
- Double
- Date
[3] 字段长度“始终”确保输入参数(HTTP 请求参数或 cookie 值)有最小长度和/或最大长度的限制
以下是验证 userName 字段的长度是否在 8 至 20 个字符之间的示例:
<span style="font-size:14px;"> // Example to validate the field length
public Class Validator {
...
public static boolean validateLength(String www.wxinxin.cc value, int minLength, int maxLength) {
String validatedValue = http://www.mamicode.com/value;
if (!validateRequired(value)) {
validatedValuehttp://www.mamicode.com/= "";
}
return (validatedValue.length() >= minLength &&
validatedValue.length(www.lxinyul.cc) <= maxLength);
}
...
}
...
String userName = request.getParameter("userName");
if (Validator.validateRequired(userName)) {
if (Validator.validateLength(userName, 8, 20)) {
// userName is valid, continue further processing
...
}
}
</span>
[4] 字段范围
始终确保输入参数是在由功能需求定义的范围内。以下是验证输入 numberOfChoices 是否在 10 至 20 之间的示例:
<span style="font-family:Arial;"><span style="line-height: 26px; white-space: pre-wrap;"><span style="font-size:14px;"> // Example to validate the field range
public Class Validator {
public static boolean validateRange(int www.ycyc66.cn value, int min, int max) {
return (value >= min && value <= max);
}
}
String fieldValue = http://www.mamicode.com/request.getParameter("numberOfChoices");
if (Validator.validateRequired(fieldValue)) {
if (Validator.validateInt(fieldValue)) {
int numberOfChoices = Integer.parseInt(fieldValue);
if (Validator.validateRange(numberOfChoices, 10, 20)) {
// numberOfChoices is valid, continue processing request.
}
}
}
</span></span><p style="word-wrap: break-word;"><span style="font-family:Arial;"><span style="line-height: 26px; white-space: pre-wrap;"></span></span></span></p>
[5] 字段选项
Web 应用程序通常会为用户显示一组可供选择的选项(例如,使用 SELECT HTML 标记),但不能执行服务器端验证以确保选定的值是其中一个允许的选项。请记住,恶意用户能够轻易修改任何选项值。始终针对由功能需求定义的受允许的选项来验证选定的用户值。以下是针对允许的选项列表来验证用户选择的示例:
<span style="font-size:14px;"> // Example to validate www.ludipt77.cn user selection against a list of options
public Class Validator {
...
public static boolean validateOption(Object[] options, Object value) {
boolean isValidValue = http://www.mamicode.com/false;
try {
List list = Arrays.asList(options);
if (list != null) {
isValidValue = http://www.mamicode.com/list.contains(value);
}
} catch (Exception e) {
}
return isValidValue;
}
}
// Allowed options
String[] options = {"option1", "option2", www.yinb666.cn "option3");
// Verify that the user selection is one of the allowed options
String userSelection = request.getParameter("userSelection");
if (Validator.validateOption(options, userSelection)) {
// valid user selection, continue processing request
<p> }</p><p></span></p>

[6] 字段模式
始终检查用户输入与由功能需求定义的模式是否匹配。例如,如果 userName 字段应仅允许字母数字字符,且不区分大小写,那么请使用以下正则表达式:^[a-zA-Z0-9]*$。 Java 1.3 或更早的版本不包含任何正则表达式包。建议将“Apache 正则表达式包”(请参阅以下“资源”)与 Java 1.3 一起使用,以解决该缺乏支持的问题。以下是执行正则表达式验证的示例:
<span style="font-size:14px;"> // Example to validate that a given value matches a specified pattern
// using the Apache regular expression package
import org.apache.regexp.RE;
import org.apache.regexp.RESyntaxException;
public Class Validator {
...
public static boolean matchPattern(String value, String expression) {
boolean match = false;
if (validateRequired(expression)) {
RE r = new RE(expression);
match = r.match(value);
}
return match;
}
}
// Verify that the userName request parameter is alpha-numeric
String userName = request.getParameter("userName");
if (Validator.matchPattern(userName, "^[a-zA-Z0-9]*$")) {
// userName is valid, continue processing request
}
</span>
Java 1.4 引进了一种新的正则表达式包(java.util.regex)。以下是使用新的 Java 1.4 正则表达式包的 Validator.matchPattern 修订版:
<span style="font-family:Arial;"><span style="line-height: 26px; white-space: pre-wrap;"><span style="font-size:14px;"> // Example to validate that a given value matches a specified pattern
// using the Java 1.4 regular expression package
import java.util.regex.Pattern;
import java.util.regexe.Matcher;
public Class Validator {
public static boolean matchPattern(String value, String expression) {
boolean match = false;
if (validateRequired(expr www.yxin7.com ession)) {
match = Pattern.matches(expression, value);
}
return match;
}
</span></span><p style="font-family: Arial; line-height: 26px; white-space: pre-wrap;"> }
</span></p>
<h3 style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; widows: 1;"><span style="font-size: 14px;">
<pre code_snippet_id="1915297" snippet_file_name="blog_20161007_8_9629753" class="httpMessage" name="code" style="white-space: pre-wrap; word-wrap: break-word; font-size: 14px; line-height: 26px; widows: 1; background-color: rgb(255, 255, 255);"></span></h3>

[7] cookie 值
使用 javax.servlet.http.Cookie 对象来验证 cookie 值。适用于 cookie 值的相同的验证规则(如上所述)取决于应用程序需求(如验证必需值、验证长度等)。以下是验证必需 cookie 值的示例:
<span style="font-size:14px;"> // Example to validate a required cookie value
// First retrieve all available cookies submitted in the HTTP request
Cookie[] cookies = request.getCookies();
if (cookies != null) {
// find the "user" cookie
for (int i=0; i<cookies.length; ++i) {
if (cookies[i].getName().equals("user")) {
// validate the cookie value
if (Validator.validateRequired(cookies[i].getValue()) {
// valid www.tyff688.com cookie value, continue processing request
}
}
}
}
<p style="font-family: Arial; line-height: 26px;"></span>
</p>

[8] HTTP 响应
[8-1] 过滤用户输入要保护应用程序免遭跨站点脚本编制的攻击,开发者应通过将敏感字符转换为其对应的字符实体来清理 HTML。这些是 HTML 敏感字符:< > " ‘ % ; ) ( & +以下为以下示例通过将敏感字符转换为其对应的字符实体来过滤指定字符串:
<span style="font-size:14px;"> // Example to filter sensitive data to prevent cross-site scripting
public Class Validator {
...
public static String filter(String value) {
if (value =http://www.mamicode.com/= null) {
return null;
}
StringBuffer result = new StringBuffer(value.length());
for (int i=0; i<value.length(); ++i) {
switch (value.charAt(i)) {
case ‘<‘:
result.append("&lt;");
break;
case ‘>‘:
result.append("&gt;");
break;
case ‘"‘:
result.append("&quot;");
break;
case ‘\‘‘:
result.append("&#39;");
break;
case ‘%‘:
result.append("&#37;");
break;
case ‘;‘:
result.append("&#59;");
break;
case ‘(‘:
result.append("&#40;");
break;
case ‘)‘:
result.append("&#41;");
break;
case ‘&‘:
result.append("&amp;");
break;
case ‘+‘:
result.append("&#43;");
break;
default:
result.append(value.charAt(i));
break;
}
return result;
}
...
}
...
// Filter the HTTP response using Validator.filter
PrintWriter out = response.getWriter();
// set output response
out.write(Validator.filter(response));
out.close();
</span>
Java Servlet API 2.3 引进了“过滤器”,它支持拦截和转换 HTTP 请求或响应。以下为使用 Validator.filter 来用“Servlet 过滤器”清理响应的示例:
<span style="font-size:14px;"> // Example to filter all sensitive characters in the HTTP response using a Java Filter.
// This example is for illustration purposes since it will filter all content in the response, including HTML tags!
public class SensitiveCharsFilter implements Filter {
...
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {

PrintWriter out = response.getWriter();
ResponseWrapper wrapper = new ResponseWrapper((HttpServletResponse)response);
chain.doFilter(request, wrapper);

CharArrayWriter caw = new CharArrayWriter();
caw.write(Validator.filter(wrapper.toString()));

response.setContentType("text/html");
response.setContentLength(caw.toString().length());
out.write(caw.toString());
out.close();
}
public class CharResponseWrapper extends HttpServletResponseWrapper {
private CharArrayWriter output;

public String toString() {
return output.toString();
}
public CharResponseWrapper(HttpServletResponse response){
super(response);
output = new CharArrayWriter();
}
public PrintWriter getWriter(){
return new PrintWriter(output);
}
}
}
}
</span>
[8-2] 保护 cookie
在 cookie 中存储敏感数据时,确保使用 Cookie.setSecure(布尔标志)在 HTTP 响应中设置 cookie 的安全标志,以指导浏览器应该使用安全协议(如 HTTPS 或 SSL)发送 cookie。
以下为保护“用户”cookie 的示例:
<span style="font-size:14px;"> // Example to secure a cookie, i.e. instruct the browser to
// send the cookie using a secure protocol
Cookie cookie = new Cookie("user", "sensitive");
cookie.setSecure(true);
response.addCookie(cookie);
</span>

0x04 PHP
1 输入数据验证
虽然为方便用户而在客户端层上提供数据验证,但仍必须始终在服务器层上执行数据验证。客户端验证本身就不安全,因为这些验证可轻易绕过,例如,通过禁用 Javascript。需要 Web 应用程序框架,以提供服务器端实用程序例程
[1] 必需字段
“始终”检查字段不为空,并且其长度要大于零,不包括行距和后面的空格。如何验证必需字段的示例:
<span style="font-size:14px;"> // PHP example to validate required fields
function validateRequired($input) {
$pass = false;
if (strlen(trim($input))>0){
$pass = true;
}
return $pass;
}
if (validateRequired($fieldName)) {
// fieldName is valid, continue processing request
}
</span>
[2] 字段数据类型
输入的 Web 应用程序中的字段数据类型和输入参数欠佳。 例如,所有 HTTP 请求参数或 cookie 值的类型都是“字符串”。开发者负责验证输入的数据类型是否正确。
[3] 字段长度
始终”确保输入参数(HTTP 请求参数或 cookie 值)有最小长度和/或最大长度的限制。
[4] 字段范围
始终确保输入参数是在由功能需求定义的范围内。
[5] 字段选项
Web 应用程序通常会为用户显示一组可供选择的选项(例如,使用 SELECT HTML 标记),但不能执行服务器端验证以确保选定的值是其中一个允许的选项。请记住,恶意用户能够轻易修改任何选项值。始终针对由功能需求定义的受允许的选项来验证选定的用户值。
[6] 字段模式
始终检查用户输入与由功能需求定义的模式是否匹配。例如,如果 userName 字段应仅允许字母数字字符,且不区分大小写,那么请使用以下正则表达式:^[a-zA-Z0-9]+$
[7] cookie 值
适用于 cookie 值的相同的验证规则(如上所述)取决于应用程序需求(如验证必需值、验证长度等)。
[8] HTTP 响应
[8-1] 过滤用户输入要保护应用程序免遭跨站点脚本编制的攻击,开发者应通过将敏感字符转换为其对应的字符实体来清理 HTML。这些是 HTML 敏感字符:< > " ‘ % ; ) ( & +PHP 包含一些自动化清理实用程序函数,如 htmlentities():
<span style="font-size:14px;"> $input = htmlentities($input, ENT_QUOTES, ‘UTF-8‘);
</span>
此外,为了避免“跨站点脚本编制”的 UTF-7 变体,您应该显式定义响应的 Content-Type 头,例如:
<span style="font-size:14px;"> <?php
header(‘Content-Type: text/html; charset=UTF-8‘);
?>
</span>
[8-2] 保护 cookie
在 cookie 中存储敏感数据且通过 SSL 来传输时,请确保先在 HTTP 响应中设置 cookie 的安全标志。这将会指示浏览器仅通过 SSL 连接来使用该 cookie。为了保护 cookie,您可以使用以下代码示例:
<span style="font-size:14px;"> <$php

$value = "http://www.mamicode.com/some_value";
$time = time()+3600;
$path = "/application/";
$domain = ".example.com";
$secure = 1;

setcookie("CookieName", $value, $time, $path, $domain, $secure, TRUE);

XPATH 注入的介绍与代码防御