界: Input Validation and Representation

输入验证与表示问题是由元字符、交替编码和数字表示引起的。安全问题源于信任输入。这些问题包括:“Buffer Overflows”、“Cross-Site Scripting”攻击、“SQL Injection”等其他问题。

SQL Injection

Abstract
通过不可信赖的数据源输入构建动态 SQL 语句,攻击者就能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL injection 错误在以下情况下发生:

1. 数据从一个不可信赖的数据源进入程序。

2. 数据用于动态构造一个 SQL 查询。
示例 1:以下代码以动态方式构造并执行一个用来搜索某用户的清单的 SQL 查询。该查询将仅为那些与当前经过身份验证的用户同名的用户显示各项内容。


...
v_account = request->get_form_field( 'account' ).
v_reference = request->get_form_field( 'ref_key' ).

CONCATENATE `user = '` sy-uname `'` INTO cl_where.
IF v_account IS NOT INITIAL.
CONCATENATE cl_where ` AND account = ` v_account INTO cl_where SEPARATED BY SPACE.
ENDIF.
IF v_reference IS NOT INITIAL.
CONCATENATE cl_where "AND ref_key = `" v_reference "`" INTO cl_where.
ENDIF.

SELECT *
FROM invoice_items
INTO CORRESPONDING FIELDS OF TABLE itab_items
WHERE (cl_where).
...


此代码计划执行的查询如下(前提是 v_account 和 v_reference 不为空):


SELECT *
FROM invoice_items
INTO CORRESPONDING FIELDS OF TABLE itab_items
WHERE user = sy-uname
AND account = <account>
AND ref_key = <reference>.


但是,此查询是通过连接一个不变的基查询字符串和一个用户输入字符串动态构造而成的,所以,此查询会成为 SQL 注入攻击的目标对象。如果攻击者为 v_reference 输入字符串 "abc` OR MANDT NE `+",为 v_account 输入字符串“1000”,则该查询会变成:


SELECT *
FROM invoice_items
INTO CORRESPONDING FIELDS OF TABLE itab_items
WHERE user = sy-uname
AND account = 1000
AND ref_key = `abc` OR MANDT NE `+`.


附加 OR MANDT NE `+` 条件会导致 WHERE 子句的估值永远为 true,因为,客户端字段永远不会等于 literal +,因此该查询在逻辑上等同于一个更为简化的查询:


SELECT * FROM invoice_items
INTO CORRESPONDING FIELDS OF TABLE itab_items.


通常,查询必须仅返回已通过身份验证的用户所拥有的条目,而通过以这种方式简化查询,攻击者就可以规避这一要求。现在,查询会返回存储在 invoice_items 表中的所有条目,而不论指定用户是谁。

示例 2:在此示例中,我们将考虑在允许员工更新地址的程序中使用 ADBC API。


PARAMETERS: p_street TYPE string,
p_city TYPE string.

Data: v_sql TYPE string,
stmt TYPE REF TO CL_SQL_STATEMENT.

v_sql = "UPDATE EMP_TABLE SET ".

"Update employee address. Build the update statement with changed details
IF street NE p_street.
CONCATENATE v_sql "STREET = `" p_street "`".
ENDIF.
IF city NE p_city.
CONCATENATE v_sql "CITY = `" p_city "`".
ENDIF.

l_upd = stmt->execute_update( v_sql ).



如果心怀不满的员工为参数 p_street 输入类似 "ABC` SALARY = `1000000" 的字符串,则应用程序会使用修订后的薪资更新数据库!

避免 SQL injection 攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

— 把没有被黑名单引用的值作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动去除 SQL 查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL injection 攻击。

References
[1] SAP OSS notes 1520356, 1487337, 1502272 and related notes.
[2] S. J. Friedl SQL Injection Attacks by Example
[3] P. Litwin Stop SQL Injection Attacks Before They Stop You MSDN Magazine
[4] P. Finnigan SQL Injection and Oracle, Part One Security Focus
[5] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press
desc.dataflow.abap.sql_injection
Abstract
通过不可信赖的数据源输入构建动态 SQL 语句,攻击者就能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL injection 错误在以下情况下发生:

1. 数据从一个不可信赖的数据源进入程序。



2. 数据用于动态地构造一个 SQL 查询。

例 1:以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有者与被授予权限的当前用户一致的条目。


...
var params:Object = LoaderInfo(this.root.loaderInfo).parameters;
var username:String = String(params["username"]);
var itemName:String = String(params["itemName"]);
var query:String = "SELECT * FROM items WHERE owner = " + username + " AND itemname = " + itemName;

stmt.sqlConnection = conn;
stmt.text = query;
stmt.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 表中的所有条目,而不论其指定所有者是谁。

示例 2:此示例说明了将不同的恶意值传递给Example 1.中构造和执行的查询所带来的影响。如果一个用户名为 wiley 的攻击者为 itemName 输入字符串“name'; DELETE FROM items; --”,则该查询就会变为以下两个查询:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';

DELETE FROM items;

--'


众多数据库服务器,其中包括 Microsoft(R) SQL Server 2000,都可以一次性执行多条用分号分隔的 SQL 指令。对于那些不允许运行用分号分隔的批量指令的数据库服务器,比如 Oracle 和其他数据库服务器,攻击者输入的这个字符串只会导致错误;但是在那些支持这种操作的数据库服务器上,攻击者可能会通过执行多条指令而在数据库上执行任意命令。

注意末尾的一对连字符 (--);这在大多数数据库服务器上都表示该语句剩余部分将视为注释,不会加以执行 [4]。在这种情况下,可通过注释字符删除修改后的查询遗留的末尾单引号。而在不允许通过这种方式使用注释的数据库上,攻击者通常仍可使用类似于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';


避免 SQL injection 攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

— 把没有被黑名单引用的值作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动去除 SQL 查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL injection 攻击。

防范 SQL injection 攻击的另外一种常用方式是使用存储过程。虽然存储过程可以阻止某些类型的 SQL injection 攻击,但是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL injection 的常用方式是限制可作为参数传入的指令类型。但是,有许多方法都可以绕过这一限制,许多危险的表达式仍可以传入存储过程。所以再次强调,存储过程在某些情况下可以避免这种攻击,但是并不能完全保护您的应用系统抵御 SQL injection 的攻击。
References
[1] S. J. Friedl SQL Injection Attacks by Example
[2] P. Litwin Stop SQL Injection Attacks Before They Stop You MSDN Magazine
[3] P. Finnigan SQL Injection and Oracle, Part One Security Focus
[4] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press
desc.dataflow.actionscript.sql_injection
Abstract
通过不可信赖的数据源输入构建动态 SQL 语句,攻击者就能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL injection 错误在以下情况下发生:

1. 数据从一个不可信赖的数据源进入程序。

2. 数据用于动态地构造一个 SQL 查询。
示例 1:以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目 owner 与被授予权限的当前用户一致的条目。


...
string userName = ctx.getAuthenticatedUserName();
string query = "SELECT * FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ ItemName.Text + "'";
sda = new SqlDataAdapter(query, conn);
DataTable dt = new DataTable();
sda.Fill(dt);
...


查询计划执行以下代码:


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 表中的所有条目,而不论其指定所有者是谁。

示例 2:此示例说明了将不同的恶意值传递给Example 1.中构造和执行的查询所带来的影响。如果一个用户名为 wiley 的攻击者为 itemName 输入字符串“name'); DELETE FROM items; --”,则该查询就会变为以下两个查询:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';

DELETE FROM items;

--'


众多数据库服务器,其中包括 Microsoft(R) SQL Server 2000,都可以一次性执行多条用分号分隔的 SQL 指令。对于那些不允许运行用分号分隔的批量指令的数据库服务器,比如 Oracle 和其他数据库服务器,攻击者输入的这个字符串只会导致错误;但是在那些支持这种操作的数据库服务器上,攻击者可能会通过执行多条指令而在数据库上执行任意命令。

注意末尾的一对连字符 (--);这在大多数数据库服务器上都表示该语句剩余部分将视为注释,不会加以执行 [4]。在这种情况下,可通过注释字符删除修改后的查询遗留的末尾单引号。而在不允许通过这种方式使用注释的数据库上,攻击者通常仍可使用类似于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';


避免 SQL injection 攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

— 把没有被黑名单引用的值作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动去除 SQL 查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL injection 攻击。

防范 SQL injection 攻击的另外一种常用方式是使用存储过程。虽然存储过程可以阻止某些类型的 SQL injection 攻击,但是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL injection 的常用方式是限制可作为参数传入的指令类型。但是,有许多方法都可以绕过这一限制,许多危险的表达式仍可以传入存储过程。所以再次强调,存储过程在某些情况下可以避免这种攻击,但是并不能完全保护您的应用系统抵御 SQL injection 的攻击。
References
[1] S. J. Friedl SQL Injection Attacks by Example
[2] P. Litwin Stop SQL Injection Attacks Before They Stop You MSDN Magazine
[3] P. Finnigan SQL Injection and Oracle, Part One Security Focus
[4] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press
desc.dataflow.dotnet.sql_injection
Abstract
通过不可信赖的数据源输入构建动态 SQL 语句,攻击者就能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL injection 错误在以下情况下发生:

1. 数据从一个不可信赖的数据源进入程序。

2. 数据用于动态地构造一个 SQL 查询。
例 1:以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有者与被授予权限的当前用户一致的条目。


...
ctx.getAuthUserName(&userName); {
CString query = "SELECT * FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ request.Lookup("item") + "'";
dbms.ExecuteSQL(query);
...
例 2:此外,SQLite 使用以下代码可以获得类似的结果:


...
sprintf (sql, "SELECT * FROM items WHERE owner='%s' AND itemname='%s'", username, request.Lookup("item"));
printf("SQL to execute is: \n\t\t %s\n", sql);
rc = sqlite3_exec(db,sql, NULL,0, &err);
...


查询计划执行以下代码:


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 表中的所有条目,而不论其指定所有者是谁。

示例 3:此示例说明了将不同的恶意值传递给Example 1.中构造和执行的查询所带来的影响。如果一个用户名为 wiley 的攻击者为 itemName 输入字符串“name'); DELETE FROM items; --”,则该查询就会变为以下两个查询:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';

DELETE FROM items;

--'


众多数据库服务器,其中包括 Microsoft(R) SQL Server 2000,都可以一次性执行多条用分号分隔的 SQL 指令。对于那些不允许运行用分号分隔的批量指令的数据库服务器,比如 Oracle 和其他数据库服务器,攻击者输入的这个字符串只会导致错误;但是在那些支持这种操作的数据库服务器上,攻击者可能会通过执行多条指令而在数据库上执行任意命令。

注意末尾的一对连字符 (--);这在大多数数据库服务器上都表示该语句剩余部分将视为注释,不会加以执行 [4]。在这种情况下,注释字符对修改的查询中遗留的结尾单引号起作用。而在不允许通过这种方式使用注释的数据库上,攻击者通常仍可使用类似于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';


避免 SQL injection 攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

— 把没有被黑名单引用的值作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动去除 SQL 查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL injection 攻击。

防范 SQL injection 攻击的另外一种常用方式是使用存储过程。虽然存储过程可以阻止某些类型的 SQL injection 攻击,但是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL injection 的常用方式是限制可作为参数传入的指令类型。但是,有许多方法都可以绕过这一限制,许多危险的表达式仍可以传入存储过程。所以再次强调,存储过程在某些情况下可以避免这种攻击,但是并不能完全保护您的应用系统抵御 SQL injection 的攻击。
References
[1] S. J. Friedl SQL Injection Attacks by Example
[2] P. Litwin Stop SQL Injection Attacks Before They Stop You MSDN Magazine
[3] P. Finnigan SQL Injection and Oracle, Part One Security Focus
[4] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press
[5] Parameterized CRecordset and CDatabase for SQL Server
[6] Parameterizing a Recordset Microsoft
[7] ODBC API Reference: SQLNumParams() Microsoft
[8] ODBC API Reference: SQLBindParameter() Microsoft
[9] OLE DB Reference: ICommandWithParameters Microsoft
desc.dataflow.cpp.sql_injection
Abstract
通过不可信赖的数据源输入构建动态 SQL 语句,攻击者就能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL injection 错误在以下情况下发生:

1. 数据从一个不可信赖的数据源进入程序。

2. 数据用于动态构造一个 SQL 查询。
例 1:以下代码动态地构造并执行了一个 SQL 查询,该查询用来搜索与指定名称相匹配的项。该查询将仅为那些与当前经过身份验证的用户同名的所有者显示各项内容。


...
ACCEPT USER.
ACCEPT ITM.
MOVE "SELECT * FROM items WHERE owner = '" TO QUERY1.
MOVE "' AND itemname = '" TO QUERY2.
MOVE "'" TO QUERY3.

STRING
QUERY1, USER, QUERY2, ITM, QUERY3 DELIMITED BY SIZE
INTO QUERY
END-STRING.

EXEC SQL
EXECUTE IMMEDIATE :QUERY
END-EXEC.
...


该代码要执行的查询如下:


SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;


但是,由于这个查询是动态构造的,由一个不变的基查询字符串和一个用户输入字符串连接而成,因此只有在 itemName 不包含单引号字符时,才会正确执行这一查询。如果一个用户名为 wiley 的攻击者为 itm 输入字符串“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 表中的所有条目,而不论其指定所有者是谁。

示例 2:在此示例中,我们会考查将不同的恶意值传递给Example 1.中构造和执行的查询所带来的影响。如果一个用户名为 wiley 的攻击者为 itemName 输入字符串“name'; DELETE FROM items; --”,则该查询就会变为以下两个查询:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';

DELETE FROM items;

--'


众多数据库服务器,其中包括 Microsoft(R) SQL Server 2000,都可以一次性执行多条用分号分隔的 SQL 指令。对于 Oracle 服务器和不允许批量执行用分号分隔的指令的其他数据库服务器,这个攻击字符串会导致错误;在那些支持批量执行的数据库上,这种攻击会使得攻击者能够对数据库执行任意命令。

注意末尾的一对连字符(--);这在大多数数据库服务器上都表示该语句剩余部分将视为注释,不会加以执行 [4]。在这种情况下,可通过注释删除修改后的查询遗留的末尾单引号。而在不允许通过这种方式使用注释的数据库上,攻击者通常仍可使用类似于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';


避免 SQL injection 攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

— 把没有被黑名单引用的值作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动去除 SQL 查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL injection 攻击。

防范 SQL injection 攻击的另外一种常用方式是使用存储过程。虽然存储过程可以阻止某些类型的 SQL injection 攻击,但是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL injection 的常用方式是限制可作为参数传入的指令类型。但是,有许多方法都可以绕过这一限制,许多危险的表达式仍可以传入存储过程。所以再次强调,存储过程在某些情况下可以避免这种攻击,但是并不能完全保护您的应用系统抵御 SQL injection 的攻击。
References
[1] S. J. Friedl SQL Injection Attacks by Example
[2] P. Litwin Stop SQL Injection Attacks Before They Stop You MSDN Magazine
[3] P. Finnigan SQL Injection and Oracle, Part One Security Focus
[4] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press
desc.dataflow.cobol.sql_injection
Abstract
通过不可信赖的数据源输入构建动态 SQL 语句,攻击者就能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL injection 错误在以下情况下发生:

1. 数据从一个不可信赖的数据源进入程序。

2. 数据用于动态构造一个 SQL 查询。
例 1:以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有者与被授予权限的当前用户一致的条目。


...
<cfquery name="matchingItems" datasource="cfsnippets">
SELECT * FROM items
WHERE owner='#Form.userName#'
AND itemId=#Form.ID#
</cfquery>
...


查询计划执行以下代码:


SELECT * FROM items
WHERE owner = <userName>
AND itemId = <ID>;


但是,由于这个查询是动态构造的,由一个不变的基查询字符串和一个用户输入字符串连接而成,因此只有在 Form.ID 不包含单引号字符时,才会正确执行这一查询。如果一个用户名为 wiley 的攻击者为 Form.ID 输入字符串“name' OR 'a'='a”,那么查询就会变成:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemId = 'name' OR 'a'='a';


附加条件 OR 'a'='a' 会使 where 从句永远评估为 true,因此该查询在逻辑上将等同于一个更为简化的查询:


SELECT * FROM items;


通常,查询必须仅返回已通过身份验证的用户所拥有的条目,而通过以这种方式简化查询,攻击者就可以规避这一要求。现在,查询会返回存储在 items 表中的所有条目,而不论其指定所有者是谁。

示例 2:此示例说明了将不同的恶意值传递给Example 1.中构造和执行的查询所带来的影响。如果一个用户名为 hacker 的攻击者为 Form.ID 输入字符串“hacker'); DELETE FROM items; --”,则该查询就会变为以下两个查询:


SELECT * FROM items
WHERE owner = 'hacker'
AND itemId = 'name';

DELETE FROM items;

--'


众多数据库服务器,其中包括 Microsoft(R) SQL Server 2000,都可以一次性执行多条用分号分隔的 SQL 指令。对于那些不允许运行用分号分隔的批量指令的数据库服务器,比如 Oracle 和其他数据库服务器,攻击者输入的这个字符串只会导致错误;但是在那些支持这种操作的数据库服务器上,攻击者可能会通过执行多条指令而在数据库上执行任意命令。

注意末尾的一对连字符 (--);这在大多数数据库服务器上都表示该语句剩余部分将视为注释,不会加以执行 [4]。在这种情况下,可通过注释字符删除修改后的查询遗留的末尾单引号。而在不允许通过这种方式使用注释的数据库上,攻击者通常仍可使用类似于Example 1.中所用的技巧进行攻击。如果攻击者输入字符串“name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a”,将创建以下三个有效语句:


SELECT * FROM items
WHERE owner = 'hacker'
AND itemId = 'name';

DELETE FROM items;

SELECT * FROM items WHERE 'a'='a';


避免 SQL injection 攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

— 把没有被黑名单引用的值作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动去除 SQL 查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL injection 攻击。

防范 SQL injection 攻击的另外一种常用方式是使用存储过程。虽然存储过程可以阻止某些类型的 SQL injection 攻击,但是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL injection 的常用方式是限制可作为参数传入的指令类型。但是,有许多方法都可以绕过这一限制,许多危险的表达式仍可以传入存储过程。所以再次强调,存储过程在某些情况下可以避免这种攻击,但是并不能完全保护您的应用系统抵御 SQL injection 的攻击。
References
[1] S. J. Friedl SQL Injection Attacks by Example
[2] P. Litwin Stop SQL Injection Attacks Before They Stop You MSDN Magazine
[3] P. Finnigan SQL Injection and Oracle, Part One Security Focus
[4] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press
desc.dataflow.cfml.sql_injection
Abstract
使用 Java J2EE PersistenceAPI 执行通过来自不可信数据源的输入构建的动态 SQL 语句,这使得攻击者能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL Injection 错误会在以下情况下出现:

1.数据从一个不可信数据源进入程序。



2.数据用于动态地构造 SQL 查询。

示例 1:下列代码可动态地构建并执行一个 SQL 查询,用来搜索与指定名称相匹配的条目。该查询仅会显示条目所有者与当前经过身份验证的用户的名称一致的条目。


...
final server = await HttpServer.bind('localhost', 18081);
server.listen((request) async {
final headers = request.headers;
final userName = headers.value('userName');
final itemName = headers.value('itemName');
final query = "SELECT * FROM items WHERE owner = '"
+ userName! + "' AND itemname = '"
+ itemName! + "'";
db.query(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 表中的所有条目,而不论其指定所有者是谁。

示例 2:此示例说明了将不同的恶意值传递给Example 1.中构造和执行的查询所带来的影响。如果一个用户名为 wiley 的攻击者为 itemName 输入字符串“name'; DELETE FROM items; --”,则该查询就会变为以下两个查询:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';

DELETE FROM items;

--'


包括 Microsoft(R) SQL Server 2000 在内的很多数据库服务器都可以一次性执行多条用分号分隔的 SQL 语句。在不允许批量执行用分号分隔的语句的 Oracle 和其他数据库服务器上,此攻击字符串只会导致错误;但是在支持批量执行的数据库上,此类型攻击可以使攻击者针对数据库执行任意命令。

注意末尾的一对连字符 (--);这在大多数数据库服务器上都表示该语句剩余部分将视为注释,不会加以执行 [4]。在这种情况下,可通过注释字符删除修改后的查询遗留的末尾单引号。而在不允许通过这种方式使用注释的数据库上,攻击者通常仍可使用类似于 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';


阻止 SQL Injection 攻击的一种传统方法是将其作为一种输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列表中的潜在恶意值(拒绝列表)。检查允许列表是一种有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的拒绝列表实现方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

- 将未引用的字段作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动转义 SQL 查询输入中的字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL Injection 攻击。

防范 SQL Injection 攻击的另外一种常用解决方法是使用存储过程。虽然存储过程可以阻止某些类型的 SQL Injection 攻击,但是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL Injection 攻击的常用方式是限制可传入存储过程参数的语句类型。但是,有许多方法都可以绕过这一限制,许多危险的语句仍可以传入存储过程。所以再次强调,存储过程在某些情况下可以避免一些类型的漏洞,但是并不能完全保护您的应用程序免受 SQL Injection 攻击。
desc.dataflow.dart.sql_injection
Abstract
通过不可信赖的数据源的输入构建动态 SQL 语句,攻击者就能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL Injection 错误会在以下情况下出现:

1.数据从一个不可信数据源进入程序。

2.数据用于动态地构造 SQL 查询。
示例 1:下列代码可动态地构建并执行一个 SQL 查询,用来搜索与指定名称相匹配的条目。该查询仅会显示条目所有者与当前经过身份验证的用户的名称一致的条目。


...
rawQuery := request.URL.Query()
username := rawQuery.Get("userName")
itemName := rawQuery.Get("itemName")
query := "SELECT * FROM items WHERE owner = " + username + " AND itemname = " + itemName + ";"

db.Exec(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 表中的所有条目,而不论其指定所有者是谁。

示例 2:此示例说明了将不同的恶意值传递给Example 1.中构造和执行的查询所带来的影响。如果一个用户名为 wiley 的攻击者为 itemName 输入字符串“name'; DELETE FROM items; --”,则该查询就会变为以下两个查询:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';

DELETE FROM items;

--'


许多数据库(包括 Microsoft(R) SQL Server 2000)允许同时执行由多个用分号分隔的 SQL 语句。在不允许批量执行用分号分隔的语句的 Oracle 和其他数据库服务器上,此攻击字符串只会导致错误;但是在支持批量执行的数据库上,此类型攻击可以使攻击者针对数据库执行任意命令。

注意末尾的一对连字符 (--);这在大多数数据库服务器上都表示该语句剩余部分将视为注释,不会加以执行。[4]。在这种情况下,可通过注释字符删除修改后的查询遗留的末尾单引号。而在不允许通过这种方式使用注释的数据库上,攻击者通常仍可使用类似于 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';


避免 SQL injection 攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

- 将未引用的字段作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动转义 SQL 查询输入中的字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL Injection 攻击。

防范 SQL Injection 攻击的另外一种常用解决方法是使用存储过程。虽然存储过程可以阻止某些类型的 SQL Injection 攻击,但是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL Injection 攻击的常用方式是限制可传入存储过程参数的语句类型。但是,有许多方法都可以绕过这一限制,许多危险的语句仍可以传入存储过程。所以再次强调,存储过程可以避免一些漏洞,但是并不能完全确保您的应用程序不受 SQL Injection 攻击。
References
[1] S. J. Friedl SQL Injection Attacks by Example
[2] P. Litwin Stop SQL Injection Attacks Before They Stop You MSDN Magazine
[3] P. Finnigan SQL Injection and Oracle, Part One Security Focus
[4] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press
desc.dataflow.golang.sql_injection
Abstract
通过不可信赖的数据源输入构建动态 SQL 语句,攻击者就能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL injection 错误在以下情况下发生:

1. 数据从一个不可信赖的数据源进入程序。



2. 数据用于动态地构造一个 SQL 查询。

例 1:以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有者与被授予权限的当前用户一致的条目。


...
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 表中的所有条目,而不论其指定所有者是谁。

示例 2:此示例说明了将不同的恶意值传递给Example 1.中构造和执行的查询所带来的影响。如果一个用户名为 wiley 的攻击者为 itemName 输入字符串“name'; DELETE FROM items; --”,则该查询就会变为以下两个查询:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';

DELETE FROM items;

--'


众多数据库服务器,其中包括 Microsoft(R) SQL Server 2000,都可以一次性执行多条用分号分隔的 SQL 指令。对于那些不允许运行用分号分隔的批量指令的数据库服务器,比如 Oracle 和其他数据库服务器,攻击者输入的这个字符串只会导致错误;但是在那些支持这种操作的数据库服务器上,攻击者可能会通过执行多条指令而在数据库上执行任意命令。

注意末尾的一对连字符 (--);这在大多数数据库服务器上都表示该语句剩余部分将视为注释,不会加以执行 [4]。在这种情况下,可通过注释字符删除修改后的查询遗留的末尾单引号。而在不允许通过这种方式使用注释的数据库上,攻击者通常仍可使用类似于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';


有些人认为在移动世界中,典型的 Web 应用程序漏洞(如 SQL injection)是无意义的 -- 为什么用户要攻击自己?但是,谨记移动平台的本质是从各种来源下载并在相同设备上运行的应用程序。恶意软件在银行应用程序附近运行的可能性很高,它们会强制扩展移动应用程序的攻击面(包括跨进程通信)。

示例 3:以下代码会调整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);
...


避免 SQL injection 攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

— 把没有被黑名单引用的值作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动去除 SQL 查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL injection 攻击。

防范 SQL injection 攻击的另外一种常用方式是使用存储过程。虽然存储过程可以阻止某些类型的 SQL injection 攻击,但是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL injection 的常用方式是限制可作为参数传入的指令类型。但是,有许多方法都可以绕过这一限制,许多危险的表达式仍可以传入存储过程。所以再次强调,存储过程在某些情况下可以避免这种攻击,但是并不能完全保护您的应用系统抵御 SQL injection 的攻击。
References
[1] S. J. Friedl SQL Injection Attacks by Example
[2] P. Litwin Stop SQL Injection Attacks Before They Stop You MSDN Magazine
[3] P. Finnigan SQL Injection and Oracle, Part One Security Focus
[4] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press
[5] IDS00-J. Prevent SQL Injection CERT
[6] INJECT-2: Avoid dynamic SQL Oracle
desc.dataflow.java.sql_injection
Abstract
通过不可信赖的数据源输入构建动态 SQL 语句,攻击者就能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL injection 错误在以下情况下发生:

1. 数据从一个不可信赖的数据源进入程序。



2. 数据用于动态地构造一个 SQL 查询。

例 1:以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有者与被授予权限的当前用户一致的条目。


...
var username = document.form.username.value;
var itemName = document.form.itemName.value;
var query = "SELECT * FROM items WHERE owner = " + username + " AND itemname = " + itemName + ";";
db.transaction(function (tx) {
tx.executeSql(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 表中的所有条目,而不论其指定所有者是谁。

示例 2:此示例说明了将不同的恶意值传递给Example 1.中构造和执行的查询所带来的影响。如果一个用户名为 wiley 的攻击者为 itemName 输入字符串“name'; DELETE FROM items; --”,则该查询就会变为以下两个查询:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';

DELETE FROM items;

--'


众多数据库服务器,其中包括 Microsoft(R) SQL Server 2000,都可以一次性执行多条用分号分隔的 SQL 指令。对于那些不允许运行用分号分隔的批量指令的数据库服务器,比如 Oracle 和其他数据库服务器,攻击者输入的这个字符串只会导致错误;但是在那些支持这种操作的数据库服务器上,攻击者可能会通过执行多条指令而在数据库上执行任意命令。

注意末尾的一对连字符 (--);这在大多数数据库服务器上都表示该语句剩余部分将视为注释,不会加以执行 [4]。在这种情况下,可通过注释字符删除修改后的查询遗留的末尾单引号。而在不允许通过这种方式使用注释的数据库上,攻击者通常仍可使用类似于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';


避免 SQL injection 攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

— 把没有被黑名单引用的值作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动去除 SQL 查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL injection 攻击。

防范 SQL injection 攻击的另外一种常用方式是使用存储过程。虽然存储过程可以阻止某些类型的 SQL injection 攻击,但是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL injection 的常用方式是限制可作为参数传入的指令类型。但是,有许多方法都可以绕过这一限制,许多危险的表达式仍可以传入存储过程。所以再次强调,存储过程在某些情况下可以避免这种攻击,但是并不能完全保护您的应用系统抵御 SQL injection 的攻击。
References
[1] S. J. Friedl SQL Injection Attacks by Example
[2] P. Litwin Stop SQL Injection Attacks Before They Stop You MSDN Magazine
[3] P. Finnigan SQL Injection and Oracle, Part One Security Focus
[4] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press
desc.dataflow.javascript.sql_injection
Abstract
通过不可信赖的数据源输入构建动态 SQL 语句,攻击者就能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL injection 错误在以下情况下发生:

1. 数据从一个不可信赖的数据源进入程序。



2. 数据用于动态地构造一个 SQL 查询。

例 1:以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有者与被授予权限的当前用户一致的条目。


...
$userName = $_SESSION['userName'];
$itemName = $_POST['itemName'];
$query = "SELECT * FROM items WHERE owner = '$userName' AND itemname = '$itemName';";
$result = mysql_query($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 表中的所有条目,而不论其指定所有者是谁。

示例 2:此示例说明了将不同的恶意值传递给Example 1.中构造和执行的查询所带来的影响。如果一个用户名为 wiley 的攻击者为 itemName 输入字符串“name'; DELETE FROM items; --”,则该查询就会变为以下两个查询:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';

DELETE FROM items;

--'


众多数据库服务器,其中包括 Microsoft(R) SQL Server 2000,都可以一次性执行多条用分号分隔的 SQL 指令。对于那些不允许运行用分号分隔的批量指令的数据库服务器,比如 Oracle 和其他数据库服务器,攻击者输入的这个字符串只会导致错误;但是在那些支持这种操作的数据库服务器上,攻击者可能会通过执行多条指令而在数据库上执行任意命令。

注意末尾的一对连字符 (--);这在大多数数据库服务器上都表示该语句剩余部分将视为注释,不会加以执行 [4]。在这种情况下,可通过注释字符删除修改后的查询遗留的末尾单引号。而在不允许通过这种方式使用注释的数据库上,攻击者通常仍可使用类似于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';


避免 SQL injection 攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

— 把没有被黑名单引用的值作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动去除 SQL 查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL injection 攻击。

防范 SQL injection 攻击的另外一种常用方式是使用存储过程。虽然存储过程可以阻止某些类型的 SQL injection 攻击,但是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL injection 的常用方式是限制可作为参数传入的指令类型。但是,有许多方法都可以绕过这一限制,许多危险的表达式仍可以传入存储过程。所以再次强调,存储过程在某些情况下可以避免这种攻击,但是并不能完全保护您的应用系统抵御 SQL injection 的攻击。
References
[1] S. J. Friedl SQL Injection Attacks by Example
[2] P. Litwin Stop SQL Injection Attacks Before They Stop You MSDN Magazine
[3] P. Finnigan SQL Injection and Oracle, Part One Security Focus
[4] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press
desc.dataflow.php.sql_injection
Abstract
通过不可信赖的数据源输入构建动态 SQL 语句,攻击者就能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL injection 错误在以下情况下发生:

1. 数据从一个不可信赖的数据源进入程序。

2. 数据用于动态构造一个 SQL 查询。
例 1:以下代码动态地构造并执行了一个 SQL 查询,该查询用来搜索与指定名称相匹配的项。该查询将仅为那些与当前经过身份验证的用户同名的所有者显示各项内容。


procedure get_item (
itm_cv IN OUT ItmCurTyp,
usr in varchar2,
itm in varchar2)
is
open itm_cv for ' SELECT * FROM items WHERE ' ||
'owner = '''|| usr || '''' ||
' AND itemname = ''' || itm || '''';
end get_item;


该代码要执行的查询如下:


SELECT * FROM items
WHERE owner = <userName>
AND itemname = <itemName>;


但是,由于这个查询是动态构造的,由一个不变的基查询字符串和一个用户输入字符串连接而成,因此只有在 itemName 不包含单引号字符时,才会正确执行这一查询。如果一个用户名为 wiley 的攻击者为 itm 输入字符串“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 表中的所有条目,而不论其指定所有者是谁。

示例 2:在此示例中,我们会考查将不同的恶意值传递给Example 1.中构造和执行的查询所带来的影响。如果一个用户名为 wiley 的攻击者为 itemName 输入字符串“name'; DELETE FROM items; --”,则该查询就会变为以下两个查询:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';

DELETE FROM items;

--'


众多数据库服务器,其中包括 Microsoft(R) SQL Server 2000,都可以一次性执行多条用分号分隔的 SQL 指令。对于 Oracle 服务器和不允许批量执行用分号分隔的指令的其他数据库服务器,这个攻击字符串会导致错误;在那些支持批量执行的数据库上,这种攻击会使得攻击者能够对数据库执行任意命令。

注意末尾的一对连字符(--);这在大多数数据库服务器上都表示该语句剩余部分将视为注释,不会加以执行 [4]。在这种情况下,可通过注释删除修改后的查询遗留的末尾单引号。而在不允许通过这种方式使用注释的数据库上,攻击者通常仍可使用类似于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';


避免 SQL injection 攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

— 把没有被黑名单引用的值作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动去除 SQL 查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL injection 攻击。

防范 SQL injection 攻击的另外一种常用方式是使用存储过程。正如上述一系列例子所示,存储过程像其他类型的代码一样也易受到攻击。虽然存储过程有助于防止某些类型的资源盗取,但是它们无法让您的应用程序免受各种 SQL injection 攻击。
References
[1] S. J. Friedl SQL Injection Attacks by Example
[2] P. Litwin Stop SQL Injection Attacks Before They Stop You MSDN Magazine
[3] P. Finnigan SQL Injection and Oracle, Part One Security Focus
[4] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press
[5] David Litchfield Lateral SQL Injection: A New Class of Vulnerability in Oracle
desc.dataflow.sql.sql_injection
Abstract
通过不可信赖的数据源输入构建动态 SQL 语句,攻击者就能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL injection 错误在以下情况下发生:

1. 数据从一个不可信赖的数据源进入程序。



2. 数据用于动态地构造一个 SQL 查询。

例 1:以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有者与被授予权限的当前用户一致的条目。


...
userName = req.field('userName')
itemName = req.field('itemName')
query = "SELECT * FROM items WHERE owner = ' " + userName +" ' AND itemname = ' " + itemName +"';"
cursor.execute(query)
result = cursor.fetchall()
...


查询计划执行以下代码:


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 表中的所有条目,而不论其指定所有者是谁。

示例 2:此示例说明了将不同的恶意值传递给Example 1.中构造和执行的查询所带来的影响。如果一个用户名为 wiley 的攻击者为 itemName 输入字符串“name'; DELETE FROM items; --”,则该查询就会变为以下两个查询:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';

DELETE FROM items;

--'


众多数据库服务器,其中包括 Microsoft(R) SQL Server 2000,都可以一次性执行多条用分号分隔的 SQL 指令。对于那些不允许运行用分号分隔的批量指令的数据库服务器,比如 Oracle 和其他数据库服务器,攻击者输入的这个字符串只会导致错误;但是在那些支持这种操作的数据库服务器上,攻击者可能会通过执行多条指令而在数据库上执行任意命令。

注意末尾的一对连字符 (--);这在大多数数据库服务器上都表示该语句剩余部分将视为注释,不会加以执行 [4]。在这种情况下,可通过注释字符删除修改后的查询遗留的末尾单引号。而在不允许通过这种方式使用注释的数据库上,攻击者通常仍可使用类似于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';


避免 SQL injection 攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

— 把没有被黑名单引用的值作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动去除 SQL 查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL injection 攻击。

防范 SQL injection 攻击的另外一种常用方式是使用存储过程。虽然存储过程可以阻止某些类型的 SQL injection 攻击,但是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL injection 的常用方式是限制可作为参数传入的指令类型。但是,有许多方法都可以绕过这一限制,许多危险的表达式仍可以传入存储过程。所以再次强调,存储过程在某些情况下可以避免这种攻击,但是并不能完全保护您的应用系统抵御 SQL injection 的攻击。
References
[1] S. J. Friedl SQL Injection Attacks by Example
[2] P. Litwin Stop SQL Injection Attacks Before They Stop You MSDN Magazine
[3] P. Finnigan SQL Injection and Oracle, Part One Security Focus
[4] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press
desc.dataflow.python.sql_injection
Abstract
通过不可信赖的数据源输入构建动态 SQL 语句,攻击者就能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL injection 错误在以下情况下发生:

1. 数据从一个不可信赖的数据源进入程序。

在这种情况下,Fortify Static Code Analyzer(Fortify 静态代码分析器)无法确定数据源是否可信赖。

2. 数据用于动态地构造一个 SQL 查询。

例 1:以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有者与被授予权限的当前用户一致的条目。


...
userName = getAuthenticatedUserName()
itemName = params[:itemName]
sqlQuery = "SELECT * FROM items WHERE owner = '#{userName}' AND itemname = '#{itemName}'"
rs = conn.query(sqlQuery)
...


查询计划执行以下代码:


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 表中的所有条目,而不论其指定所有者是谁。

由于 Ruby 不是静态类型语言这一事实,也使得可能无法在静态类型语言中使用的 SQL 查询中实现了其他注入点。
例 2:以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有者与被授予权限的当前用户一致的条目。


...
id = params[:id]
itemName = Mysql.escape_string(params[:itemName])
sqlQuery = "SELECT * FROM items WHERE id = #{userName} AND itemname = '#{itemName}'"
rs = conn.query(sqlQuery)
...


在这种情况下,要运行的预期 SQL 查询是:


SELECT * FROM items WHERE id=<id> AND itemname = <itemName>;

这次您可以看到,我们已经防止攻击者在 itemName 中指定单引号,并且似乎已经避免了 SQL injection 漏洞。然而,由于 Ruby 不是静态类型语言,即使我们期望 id 是某种整数,因为这是通过用户输入来指定的,它也不一定是一个数字。如果攻击者可以将 id 的值更改为 1 OR id!=1--,因为没有检查 id 实际上是数字,SQL 查询现在变为:


SELECT * FROM items WHERE id=1 OR id!=1-- AND itemname = 'anyValue';


注意成对的连字符 (--);这在大多数数据库服务器上都表示下面的语句将作为注释使用,而不能加以执行 [4]。由于这个原因,它现在只运行一个包括以下组件的 SQL 查询:


SELECT * FROM items WHERE id=1 OR id!=1;


我们现在只选择该表中的一切内容,无论 id 的值是否等于 1,这当然相当于表中的一切内容。

许多数据库服务器允许一次执行多个用分号分隔的 SQL 语句。对于那些不允许运行用分号分隔的批量指令的数据库服务器,比如 Oracle 和其他数据库服务器,攻击者输入的这个字符串只会导致错误;但是在那些支持这种操作的数据库服务器上,攻击者可能会通过执行多条指令而在数据库上执行任意命令。

避免 SQL injection 攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

— 把没有被黑名单引用的值作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动去除 SQL 查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL injection 攻击。

防范 SQL injection 攻击的另外一种常用方式是使用存储过程。虽然存储过程可以阻止某些类型的 SQL injection 攻击,但是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL injection 的常用方式是限制可作为参数传入的指令类型。但是,有许多方法都可以绕过这一限制,许多危险的表达式仍可以传入存储过程。所以再次强调,存储过程在某些情况下可以避免这种攻击,但是并不能完全保护您的应用系统抵御 SQL injection 的攻击。
References
[1] S. J. Friedl SQL Injection Attacks by Example
[2] P. Litwin Stop SQL Injection Attacks Before They Stop You MSDN Magazine
[3] P. Finnigan SQL Injection and Oracle, Part One Security Focus
[4] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press
desc.dataflow.ruby.sql_injection
Abstract
通过不可信赖的数据源输入构建动态 SQL 语句,攻击者就能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL Injection 错误在以下情况下发生:

1. 数据从一个不可信赖的数据源进入程序。



2. 数据用于动态地构造 SQL 查询。

示例 1: 以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的用户。 该查询将显示的项限制为那些与作为路径参数提供的用户名相匹配的所有者。


def doSQLQuery(value:String) = Action.async { implicit request =>
val result: Future[Seq[User]] = db.run {
sql"select * from users where name = '#$value'".as[User]
}
...
}


查询计划执行以下代码:


SELECT * FROM users
WHERE name = <userName>


但是,由于该查询是动态构造的,由一个常数基本查询字符串和一个用户输入字符串连接而成,因此只有在 userName 不包含单引号字符时,该查询才能正常运行。 如果一个用户名为 wiley 的攻击者为 userName 输入字符串“name' OR 'a'='a”,则该查询会变成:


SELECT * FROM users
WHERE name = 'name' OR 'a'='a';


如果添加条件 OR 'a'='a',where 子句的值将始终为 true,这样该查询在逻辑上就等同于一个更为简单的查询:


SELECT * FROM users;


通常,查询必须仅返回指定用户所拥有的用户,而通过以这种方式简化查询,攻击者就可以规避这一要求;现在,查询将返回 users 表中存储的所有条目,而不论其指定用户是谁。

避免 SQL injection 攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

- 将没有被黑名单引用的字段作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动转义 SQL 查询输入中的字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL Injection 攻击。

防范 SQL Injection 攻击的另外一种常用解决方法是使用存储过程。 虽然存储过程可以阻止某些类型的 SQL Injection 攻击,但是对于绝大多数攻击仍无能为力。 存储过程有助于避免 SQL Injection 攻击的常用方式是限制可传入存储过程参数的语句类型。 但是,有许多方法都可以绕过这一限制,许多危险的语句仍可以传入存储过程。 所以再次强调,存储过程在某些情况下可以避免一些漏洞,但是并不能完全保护您的应用程序免受 SQL Injection 攻击。
References
[1] S. J. Friedl SQL Injection Attacks by Example
[2] P. Litwin Stop SQL Injection Attacks Before They Stop You MSDN Magazine
[3] P. Finnigan SQL Injection and Oracle, Part One Security Focus
[4] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press
[5] IDS00-J. Prevent SQL Injection CERT
[6] INJECT-2: Avoid dynamic SQL Oracle
desc.dataflow.scala.sql_injection
Abstract
通过不可信赖的数据源输入构建动态 SQL 语句,攻击者就能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL Injection 错误会在以下情况下出现:

1.数据从一个不可信赖的数据源进入程序。

2.数据用于动态地构造 SQL 查询。
示例 1:下列代码可动态地构建并执行一个 SQL 查询,用来搜索与指定名称相匹配的条目。该查询仅会显示条目的 owner与当前经过身份验证的用户的名称一致的条目。


...
let queryStatementString = "SELECT * FROM items WHERE owner='\(username)' AND itemname='\(item)'"
var queryStatement: OpaquePointer? = nil
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
if sqlite3_step(queryStatement) == SQLITE_ROW {
...
}
}
...


查询计划执行以下代码:


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 表中的所有条目,而不论其指定所有者是谁。

示例 3:此示例说明了将不同的恶意值传递给Example 1.中构造和执行的查询所带来的影响。如果一个用户名为 wiley 的攻击者为 itemName 输入字符串“name'); DELETE FROM items; --”,则该查询就会变为以下两个查询:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';

DELETE FROM items;

--'


包括 Microsoft(R) SQL Server 2000 在内的很多数据库服务器都可以一次性执行多条用分号分隔的 SQL 语句。在不允许批量执行用分号分隔的语句的 Oracle 和其他数据库服务器上,此攻击字符串只会导致错误;但是在支持批量执行的数据库上,此类型攻击可以使攻击者针对数据库执行任意命令。

注意末尾的一对连字符 (--);这在大多数数据库服务器上都表示该语句剩余部分将视为注释,不会加以执行 [4]。在这种情况下,注释字符对修改的查询中遗留的结尾单引号起作用。而在不允许通过这种方式使用注释的数据库上,攻击者通常仍可使用类似于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';


避免 SQL injection 攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

- 将未引用的字段作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动转义 SQL 查询输入中的字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL Injection 攻击。

防范 SQL Injection 攻击的另外一种常用解决方法是使用存储过程。虽然存储过程可以阻止某些类型的 SQL Injection 攻击,但是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL Injection 攻击的常用方式是限制可传入存储过程参数的语句类型。但是,有许多方法都可以绕过这一限制,许多危险的语句仍可以传入存储过程。所以再次强调,存储过程在某些情况下可以避免一些漏洞,但是并不能完全保护您的应用程序免受 SQL Injection 攻击。
References
[1] S. J. Friedl SQL Injection Attacks by Example
[2] P. Litwin Stop SQL Injection Attacks Before They Stop You MSDN Magazine
[3] P. Finnigan SQL Injection and Oracle, Part One Security Focus
[4] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press
[5] Parameterized CRecordset and CDatabase for SQL Server
[6] Parameterizing a Recordset Microsoft
[7] ODBC API Reference: SQLNumParams() Microsoft
[8] ODBC API Reference: SQLBindParameter() Microsoft
[9] OLE DB Reference: ICommandWithParameters Microsoft
desc.dataflow.swift.sql_injection
Abstract
通过不可信赖的数据源输入构建动态 SQL 语句,攻击者就能够修改语句的含义或者执行任意 SQL 命令。
Explanation
SQL injection 错误在以下情况下发生:

1. 数据从一个不可信赖的数据源进入程序。



2. 数据用于动态地构造一个 SQL 查询。

例 1:以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有者与被授予权限的当前用户一致的条目。


...
username = Session("username")
itemName = Request.Form("itemName")
strSQL = "SELECT * FROM items WHERE owner = '"& userName &"' AND itemname = '" & itemName &"'"
objRecordSet.Open strSQL, strConnect, adOpenDynamic, adLockOptimistic, adCmdText
...


查询计划执行以下代码:


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 表中的所有条目,而不论其指定所有者是谁。

示例 2:此示例说明了将不同的恶意值传递给Example 1.中构造和执行的查询所带来的影响。如果一个用户名为 wiley 的攻击者为 itemName 输入字符串“name'; DELETE FROM items; --”,则该查询就会变为以下两个查询:


SELECT * FROM items
WHERE owner = 'wiley'
AND itemname = 'name';

DELETE FROM items;

--'


众多数据库服务器,其中包括 Microsoft(R) SQL Server 2000,都可以一次性执行多条用分号分隔的 SQL 指令。对于那些不允许运行用分号分隔的批量指令的数据库服务器,比如 Oracle 和其他数据库服务器,攻击者输入的这个字符串只会导致错误;但是在那些支持这种操作的数据库服务器上,攻击者可能会通过执行多条指令而在数据库上执行任意命令。

注意末尾的一对连字符 (--);这在大多数数据库服务器上都表示该语句剩余部分将视为注释,不会加以执行 [4]。在这种情况下,可通过注释字符删除修改后的查询遗留的末尾单引号。而在不允许通过这种方式使用注释的数据库上,攻击者通常仍可使用类似于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';


避免 SQL injection 攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的 SQL 语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL Injection 攻击。例如,攻击者可以:

— 把没有被黑名单引用的值作为目标
- 寻找方法以绕过某些需要转义的元字符
- 使用存储过程隐藏注入的元字符

手动去除 SQL 查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL injection 攻击。

防范 SQL injection 攻击的另外一种常用方式是使用存储过程。虽然存储过程可以阻止某些类型的 SQL injection 攻击,但是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL injection 的常用方式是限制可作为参数传入的指令类型。但是,有许多方法都可以绕过这一限制,许多危险的表达式仍可以传入存储过程。所以再次强调,存储过程在某些情况下可以避免这种攻击,但是并不能完全保护您的应用系统抵御 SQL injection 的攻击。
References
[1] S. J. Friedl SQL Injection Attacks by Example
[2] P. Litwin Stop SQL Injection Attacks Before They Stop You MSDN Magazine
[3] P. Finnigan SQL Injection and Oracle, Part One Security Focus
[4] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press
desc.dataflow.vb.sql_injection