输入验证与表示问题是由元字符、交替编码和数字表示引起的。安全问题源于信任输入。这些问题包括:“Buffer Overflows”、“Cross-Site Scripting”攻击、“SQL Injection”等其他问题。
...
String userName = ctx.getAuthenticatedUserName();
String itemName = request.getParameter("itemName");
String query = "FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ itemName + "'";
List items = sess.createQuery(query).list();
...
SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;
itemName
不包含单引号字符时,才会正确执行这一查询。如果一个用户名为 wiley
的攻击者为 itemName
输入字符串“name' OR 'a'='a
”,那么查询就会变成:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
会使 where 从句永远评估为 true,因此该查询在逻辑上将等同于一个更为简化的查询:
SELECT * FROM items;
items
表中的所有条目,而不论其指定所有者是谁。Example 1
.中构造和执行的查询所带来的影响。如果一个用户名为 wiley
的攻击者为 itemName
输入字符串“name'; DELETE FROM items; --
”,则该查询就会变为以下两个查询:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
--'
Example 1
.中所用的技巧进行攻击。如果攻击者输入字符串“name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a
”,将创建以下三个有效语句:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
#
字符来定义,如下所示:
<select id="getItems" parameterClass="MyClass" resultClass="items">
SELECT * FROM items WHERE owner = #userName#
</select>
#
字符表示 iBatis 将创建含 userName
变量的参数化查询。但是,iBatis 也允许使用 $
字符将变量直接连接到 SQL 语句,因而为发动 SQL Injection 攻击敞开了大门。
<select id="getItems" parameterClass="MyClass" resultClass="items">
SELECT * FROM items WHERE owner = #userName# AND itemname = '$itemName$'
</select>
itemName
不包含单引号字符时,该查询才能正常运行。如果一个用户名为 wiley
的攻击者为 itemName
输入字符串“name' OR 'a'='a
”,则该查询会变成:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
,where 子句的值将始终为 true,这样该查询在逻辑上就等同于一个更为简单的查询:
SELECT * FROM items;
items
表中的所有条目,而不论其指定所有者是谁。Example 1
.中构造和执行的查询所带来的影响。如果一个用户名为 wiley
的攻击者为 itemName
输入字符串“name'; DELETE FROM items; --
”,则该查询就会变为以下两个查询:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
--'
Example 1
中所用的技巧进行攻击。如果攻击者输入字符串“name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a
”,将创建以下三个有效语句:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
...
String userName = ctx.getAuthenticatedUserName();
String itemName = request.getParameter("itemName");
String sql = "SELECT * FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ itemName + "'";
Query query = pm.newQuery(Query.SQL, sql);
query.setClass(Person.class);
List people = (List)query.execute();
...
SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;
itemName
不包含单引号字符时,才会正确执行这一查询。如果一个用户名为 wiley
的攻击者为 itemName
输入字符串“name' OR 'a'='a
”,那么查询就会变成:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
会使 where 从句永远评估为 true,因此该查询在逻辑上将等同于一个更为简化的查询:
SELECT * FROM items;
items
表中的所有条目,而不论其指定所有者是谁。Example 1
.中构造和执行的查询所带来的影响。如果一个用户名为 wiley
的攻击者为 itemName
输入字符串“name'; DELETE FROM items; --
”,则该查询就会变为以下两个查询:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
--'
Example 1
.中所用的技巧进行攻击。如果攻击者输入字符串“name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a
”,将创建以下三个有效语句:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
owner
与被授予权限的当前用户一致的条目。
...
string userName = ctx.getAuthenticatedUserName();
string query = "SELECT * FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ ItemName.Text + "'";
var items = dataContext.ExecuteCommand<Item>(query);
...
SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;
itemName
不包含单引号字符时,才会正确执行这一查询。如果一个用户名为 wiley
的攻击者为 itemName
输入字符串“name' OR 'a'='a
”,那么查询就会变成:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
会使 where 从句永远评估为 true,因此该查询在逻辑上将等同于一个更为简化的查询:
SELECT * FROM items;
items
表中的所有条目,而不论其指定所有者是谁。Example 1
.中构造和执行的查询所带来的影响。如果一个用户名为 wiley
的攻击者为 itemName
输入字符串“name'); DELETE FROM items; --
”,则该查询就会变为以下两个查询:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
--'
Example 1
.中所用的技巧进行攻击。如果攻击者输入字符串“name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a
”,将创建以下三个有效语句:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
#
字符来定义,如下所示:
<select id="getItems" parameterType="domain.company.MyParamClass" resultType="MyResultMap">
SELECT *
FROM items
WHERE owner = #{userName}
</select>
#
字符并在变量名称两侧添加大括号表示 MyBatis 将创建含 userName
变量的参数化查询。但是,MyBatis 也允许使用 $
字符将变量直接连接到 SQL 语句,因而为发动 SQL Injection 攻击敞开了大门。
<select id="getItems" parameterType="domain.company.MyParamClass" resultType="MyResultMap">
SELECT *
FROM items
WHERE owner = #{userName}
AND itemname = ${itemName}
</select>
itemName
不包含单引号字符时,该查询才能正常运行。如果一个用户名为 wiley
的攻击者为 itemName
输入字符串“name' OR 'a'='a
”,则该查询会变成:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
,WHERE
子句的值将始终为 true,这样该查询在逻辑上就等同于一个更为简单的查询:
SELECT * FROM items;
items
表中的所有条目,而不论其指定所有者是谁。Example 1
.中构造和执行的查询所带来的影响。如果一个用户名为 wiley
的攻击者为 itemName
输入字符串“name'; DELETE FROM items; --
”,则该查询就会变为以下两个查询:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
--'
Example 1
中所用的技巧进行攻击。如果攻击者输入字符串“name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a
”,将创建以下三个有效语句:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
owner
与被授予权限的当前用户一致的条目。
...
string userName = ctx.GetAuthenticatedUserName();
string query = "SELECT * FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ ItemName.Text + "'";
List items = sess.CreateSQLQuery(query).List();
...
SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;
ItemName
不包含单引号字符时,才会正确执行这一查询。如果一个用户名为 wiley
的攻击者为 ItemName
输入字符串“name' OR 'a'='a
”,那么查询就会变成:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
会使 where 从句永远评估为 true,因此该查询在逻辑上将等同于一个更为简化的查询:
SELECT * FROM items;
items
表中的所有条目,而不论其指定所有者是谁。Example 1
.中构造和执行的查询所带来的影响。如果一个用户名为 wiley
的攻击者为 ItemName
输入字符串“name'; DELETE FROM items; --
”,则该查询就会变为以下两个查询:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
--'
Example 1
.中所用的技巧进行攻击。如果攻击者输入字符串“name'; DELETE FROM items; SELECT * FROM items WHERE 'a'='a
”,将创建以下三个有效语句:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
owner
与当前经过身份验证的用户的名称一致的条目。
...
string userName = identity.User;
string itemName = apiGatewayProxyRequest.QueryStringParameters['item'];
string statement = $"SELECT * FROM items WHERE owner = '{userName}' AND itemname = '{itemName}'";
var executeStatementRequest = new ExecuteStatementRequest();
executeStatementRequest.Statement = statement;
var executeStatementResponse = await dynamoDBClient.ExecuteStatementAsync(executeStatementRequest);
return displayResults(executeStatementResponse.Items);
...
SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;
itemName
不包含单引号字符时,该查询才能正常运行。如果一个用户名为 wiley
的攻击者为 itemName
输入字符串“name' OR 'a'='a
”,则该查询会变成:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
,where 子句的值将始终为 true,这样该查询在逻辑上就等同于一个更为简单的查询:owner
与当前经过身份验证的用户的名称一致的条目。
...
String userName = identity.getUser();
String itemName = apiGatewayProxyRequest.getQueryStringParameters('item');
String statement = String.format("SELECT * FROM items WHERE owner = '%s' AND itemname = '%s'", userName, itemName);
ExecuteStatementRequest executeStatementRequest = new ExecuteStatementRequest();
executeStatementRequest.setStatement(statement);
ExecuteStatementResponse executeStatementResponse = dynamoDBClient.executeStatement(executeStatementRequest);
return displayResults(executeStatementResponse.items());
...
SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;
itemName
不包含单引号字符时,该查询才能正常运行。如果一个用户名为 wiley
的攻击者为 itemName
输入字符串“name' OR 'a'='a
”,则该查询会变成:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
,where 子句的值将始终为 true,这样该查询在逻辑上就等同于一个更为简单的查询:
...
String userName = ctx.getAuthenticatedUserName();
String itemName = request.getParameter("itemName");
String query = "SELECT * FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ itemName + "'";
ResultSet rs = stmt.execute(query);
...
SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;
itemName
不包含单引号字符时,才会正确执行这一查询。如果一个用户名为 wiley
的攻击者为 itemName
输入字符串“name' OR 'a'='a
”,那么查询就会变成:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
会使 where 从句永远评估为 true,因此该查询在逻辑上将等同于一个更为简化的查询:
SELECT * FROM items;
items
表中的所有条目,而不论其指定所有者是谁。Example 1
.中构造和执行的查询所带来的影响。如果一个用户名为 wiley
的攻击者为 itemName
输入字符串“name'; DELETE FROM items; --
”,则该查询就会变为以下两个查询:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
--'
Example 1
中所用的技巧进行攻击。如果攻击者输入字符串“name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a
”,将创建以下三个有效语句:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
Example 1
以适应 Android 平台。
...
PasswordAuthentication pa = authenticator.getPasswordAuthentication();
String userName = pa.getUserName();
String itemName = this.getIntent().getExtras().getString("itemName");
String query = "SELECT * FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ itemName + "'";
SQLiteDatabase db = this.openOrCreateDatabase("DB", MODE_PRIVATE, null);
Cursor c = db.rawQuery(query, null);
...
mysql_real_escape_string()
等编码函数可避免一部分 SQL Injection 漏洞,但不能完全避免。依靠此类编码函数等同于用一个安全性较差的拒绝列表来防止 SQL Injection 攻击,并且可能允许攻击者修改语句的含义或者执行任意 SQL 命令。由于在动态解释代码的给定部分中不可能总是静态确定输入显示的位置,因此 Fortify 安全编码规则包可能会将经过验证的动态 SQL 数据显示为“SQL Injection: Poor Validation”问题,即使验证可能足以防止在该上下文中出现 SQL Injection 攻击时也是如此。mysqli_real_escape_string()
的行为。 将 SQL 模式设置为“NO_BACKSLASH_ESCAPES” 时,反斜杠字符会被视为正常字符,而不是转移字符[5]。 由于 mysqli_real_escape_string()
将此情况考虑在内,因此鉴于数据库配置,当 "
不再转义为 \"
时,以下查询易受到 SQL Injection 的攻击。
mysqli_query($mysqli, 'SET SQL_MODE="NO_BACKSLASH_ESCAPES"');
...
$userName = mysqli_real_escape_string($mysqli, $_POST['userName']);
$pass = mysqli_real_escape_string($mysqli, $_POST['pass']);
$query = 'SELECT * FROM users WHERE userName="' . $userName . '"AND pass="' . $pass. '";';
$result = mysqli_query($mysqli, $query);
...
password
字段留空并为 userName
输入 " OR 1=1;--
,则不对引号进行转义,最后的查询如下所示:
SELECT * FROM users
WHERE userName = ""
OR 1=1;
-- "AND pass="";
OR 1=1
会导致 where 子句的值始终为 true 且双连字符会导致剩余语句被视为注释,因此该查询在逻辑上就等同于一个更为简单的查询:
SELECT * FROM users;
owner
与被授予权限的当前用户一致的条目。
...
string userName = ctx.getAuthenticatedUserName();
string query = "SELECT * FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ ItemName.Text + "'";
IDataReader responseReader = new InlineQuery().ExecuteReader(query);
...
SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;
itemName
不包含单引号字符时,才会正确执行这一查询。如果一个用户名为 wiley
的攻击者为 itemName
输入字符串“name' OR 'a'='a
”,那么查询就会变成:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name' OR 'a'='a';
OR 'a'='a'
会使 where 从句永远评估为 true,因此该查询在逻辑上将等同于一个更为简化的查询:
SELECT * FROM items;
items
表中的所有条目,而不论其指定所有者是谁。Example 1
.中构造和执行的查询所带来的影响。如果一个用户名为 wiley
的攻击者为 itemName
输入字符串“name'); DELETE FROM items; --
”,则该查询就会变为以下两个查询:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
--'
Example 1
.中所用的技巧进行攻击。如果攻击者输入字符串“name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a
”,将创建以下三个有效语句:
SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
cfgfile
进行读取,并使用 strcpy()
将输入复制到 inputbuf
中。但是,该代码会错误的假定 inputbuf
始终包含 null 终止符。
#define MAXLEN 1024
...
char *pathbuf[MAXLEN];
...
read(cfgfile,inputbuf,MAXLEN); //does not null-terminate
strcpy(pathbuf,inputbuf); //requires null-terminated input
...
cfgfile
中读取的数据在磁盘上按预期以“null”结尾,则Example 1
中的代码可正常运行。但是,如果攻击者能够篡改此输入,使其不包含所需的 null
字符,则对 strcpy()
的调用将连续从内存进行复制,直到遇到任意 null
字符为止。因此,可能会溢出目标缓冲区,更有甚者,如果攻击者能够控制紧随 inputbuf
之后的内存内容,那么就会使应用程序遭受 Buffer Overflow 攻击。readlink()
对存储在缓冲区 path
上的某个符号链接名进行了扩展,以使缓冲区 buf
包含可通过该符号链接而引用的文件的绝对路径。而最终的长度值将通过 strlen()
来计算。
...
char buf[MAXPATH];
...
readlink(path, buf, MAXPATH);
int length = strlen(buf);
...
readlink()
读入 buf
的值不会以“null”结尾,Example 2
中的代码将无法正确运行。在测试过程中,类似于这样的漏洞可能不会被捕捉到,因为 buf
中未使用的内容或紧随其后的内存可能都为 null
,因此会显示 strlen()
,而这看上去似乎运行正常一样。然而,在默认情况下,strlen()
将持续遍历内存,直到在堆栈中遇到任意 null
字符为止,这会导致 length
的值远远大于 buf
的大小,从而在随后使用该值的操作中可能会造成缓冲区溢出。snprintf()
复制用户输入字符串并将其放置在多个输出字符串中。尽管与 sprintf()
相比,snprintf()
函数提供了额外的保护机制,特别是指定了最大输出大小,但当指定的输出大小大于预期输入时,该函数仍然容易发生字符串终止错误。字符串终止错误可能导致下游问题,例如内存泄漏或缓冲区溢出。
...
char no_null_term[5] = getUserInput();
char output_1[20];
snprintf(output_1, 20, "%s", no_null_term);
char output_2[20];
snprintf(output_2, 20, "%s", no_null_term);
printf("%s\n", output_1);
printf("%s\n", output_2);
...
Example 3
中的代码演示了内存泄漏。当 output_2
填充有 no_null_term
时,snprintf()
必须从 no_null_term
位置读取值,直到遇到 null 字符或达到指定的大小限制。因为 no_null_term
没有终止,snprintf
继续读入 output_1
数据,最终到达第一次调用 snprintf()
提供的 null 终止字符。output_2
的 printf()
演示了内存泄漏,其中包含 no_null_term
字符序列两次。null
字符结尾。先前的字符串处理方法往往会通过此 null
字符来确定字符串的长度。如果某个不包含 null 终止符的缓冲区被传递给其中一个函数,那么该函数就会读取到该缓冲区末尾之后的内容。ActionClass-validation.xml
中存在多个同名字段验证器定义。重复同名验证定义可能引发意外行为。
<field name="emailField">
<field-validator type="email" short-circuit="true">
<message>You must enter a value for email.</message>
</field-validator>
<field-validator type="email" short-circuit="true">
<message>Not a valid email.</message>
</field-validator>
</field>
validators.xml
中发现多个验证器定义。多个同名验证定义可能引发意外行为。validators.xml
中定义自定义验证器,再在操作验证器定义中进行使用。缺少验证器定义表明验证并非处于最新状态。validators.xml
中定义。
<validators>
<validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
</validators>
<form-validation>
<formset>
<form name="ProjectForm">
...
</form>
<form name="ProjectForm">
...
</form>
</formset>
</form-validation>