从用户控制的来源构建的 SQL 查询¶
ID: cs/sql-injection
Kind: path-problem
Security severity: 8.8
Severity: error
Precision: high
Tags:
- security
- external/cwe/cwe-089
Query suites:
- csharp-code-scanning.qls
- csharp-security-extended.qls
- csharp-security-and-quality.qls
如果使用字符串连接构建 SQL 查询,并且连接的组件包含用户输入,则用户很可能能够运行恶意数据库查询。
建议¶
通常,使用预处理语句比使用字符串连接构建完整的查询更好。预处理语句可以包含一个参数,写成问号 (?
) 或显式名称 (@parameter
),用于 SQL 查询中预期每次运行时都由不同值填充的每个部分。稍后执行查询时,必须为查询中的每个参数提供一个值。
无论参数是否可以直接追溯到用户输入,最佳做法是使用预处理语句为查询提供参数。这样做可以避免任何需要担心引用和转义的问题。
示例¶
在以下示例中,代码以三种不同的方式运行简单的 SQL 查询。
第一种方法涉及通过将用户提供的文本框值与一些字符串字面量连接来构建查询 query1
。文本框值可以包含特殊字符,因此此代码允许 SQL 注入攻击。
第二种方法使用存储过程 ItemsStoredProcedure
,该过程具有单个参数 (@category
)。然后通过调用 Parameters.Add
为该参数赋值。此版本不受注入攻击的影响,因为任何特殊字符都不会被赋予任何特殊处理。
第三种方法使用单个字符串字面量构建查询 query2
,该字面量包含一个参数 (@category
)。然后通过调用 Parameters.Add
为该参数赋值。此版本不受注入攻击的影响,因为任何特殊字符都不会被赋予任何特殊处理。
using System.Data;
using System.Data.SqlClient;
using System.Web.UI.WebControls;
class SqlInjection
{
TextBox categoryTextBox;
string connectionString;
public DataSet GetDataSetByCategory()
{
// BAD: the category might have SQL special characters in it
using (var connection = new SqlConnection(connectionString))
{
var query1 = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
+ categoryTextBox.Text + "' ORDER BY PRICE";
var adapter = new SqlDataAdapter(query1, connection);
var result = new DataSet();
adapter.Fill(result);
return result;
}
// GOOD: use parameters with stored procedures
using (var connection = new SqlConnection(connectionString))
{
var adapter = new SqlDataAdapter("ItemsStoredProcedure", connection);
adapter.SelectCommand.CommandType = CommandType.StoredProcedure;
var parameter = new SqlParameter("category", categoryTextBox.Text);
adapter.SelectCommand.Parameters.Add(parameter);
var result = new DataSet();
adapter.Fill(result);
return result;
}
// GOOD: use parameters with dynamic SQL
using (var connection = new SqlConnection(connectionString))
{
var query2 = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY="
+ "@category ORDER BY PRICE";
var adapter = new SqlDataAdapter(query2, connection);
var parameter = new SqlParameter("category", categoryTextBox.Text);
adapter.SelectCommand.Parameters.Add(parameter);
var result = new DataSet();
adapter.Fill(result);
return result;
}
}
}
参考¶
MSDN:如何:在 ASP.NET 中防止 SQL 注入。
常见弱点枚举:CWE-89。