You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#中实现SQL Prepare以防范SQL注入?

C#中防范SQL注入的实现方案(针对复杂WHERE子句场景)

核心原则

绝对禁止直接将用户输入或动态内容拼接成SQL字符串,所有变量值必须通过参数化查询处理;对于无法参数化的部分(如表名、列名),必须通过白名单校验限制范围。

具体实现步骤

1. 白名单校验动态表名、列名

表名、列名这类无法通过参数化处理的元素,必须预先定义允许访问的白名单,只有在白名单内的名称才能用于构建SQL:

  • 定义允许的表名、列名集合
  • 对传入的动态表名、选择列、IN子句的列进行校验,不在白名单内则直接抛出异常

2. 参数化处理复杂WHERE过滤子句

即使WHERE子句结构复杂(包含AND、OR、括号),所有变量值必须使用参数占位符(如@val1),不能直接拼接实际值:

  • 预先构建带参数占位符的WHERE条件模板(如(column2=@val2 OR column3=@val3 AND column4=@val4))
  • 将对应的值通过SqlParameter绑定到命令对象

3. 安全处理IN子句参数

IN子句的参数列表不能直接拼接字符串(如IN (1,2,3)),需为每个元素创建独立参数:

  • 动态生成参数占位符列表(如@inVal0,@inVal1,@inVal2)
  • 将每个IN元素绑定到对应的参数

完整代码示例

using System.Data.SqlClient;
using System.Collections.Generic;
using System.Linq;

public void ExecuteSafeQuery(string selectColumns, string tableName, string whereConditionTemplate, List<SqlParameter> whereParams, string inColumn, IEnumerable<int> inValues)
{
    // 定义允许访问的表和列白名单
    var allowedTables = new HashSet<string> { "Users", "Orders", "Products" };
    var allowedColumns = new HashSet<string> { "Id", "UserName", "Age", "OrderId", "ProductName" };

    // 校验表名合法性
    if (!allowedTables.Contains(tableName.Trim()))
        throw new ArgumentException("非法的表访问请求");

    // 校验选择列合法性(简单拆分,复杂场景需解析SQL语法)
    foreach (var column in selectColumns.Split(',').Select(c => c.Trim()))
    {
        if (!allowedColumns.Contains(column))
            throw new ArgumentException($"禁止查询列: {column}");
    }

    // 校验IN子句的列合法性
    if (!allowedColumns.Contains(inColumn.Trim()))
        throw new ArgumentException($"禁止使用的IN列: {inColumn}");

    // 构建IN子句的参数占位符
    var inParamNames = inValues.Select((_, index) => $"@inVal{index}").ToList();
    string inClauseParams = string.Join(",", inParamNames);

    // 拼接最终SQL(仅拼接白名单校验后的表/列名、参数占位符)
    string finalSql = $"SELECT {selectColumns} FROM {tableName} WHERE {whereConditionTemplate} AND {inColumn} IN ({inClauseParams})";

    using (var connection = new SqlConnection("Your_Connection_String"))
    {
        connection.Open();
        using (var command = new SqlCommand(finalSql, connection))
        {
            // 添加WHERE条件的参数
            command.Parameters.AddRange(whereParams.ToArray());

            // 添加IN子句的参数
            for (int i = 0; i < inValues.Count(); i++)
            {
                command.Parameters.AddWithValue($"@inVal{i}", inValues.ElementAt(i));
            }

            // 执行查询并处理结果
            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    // 读取数据逻辑,例如:
                    // int id = reader.GetInt32(reader.GetOrdinal("Id"));
                    // string name = reader.GetString(reader.GetOrdinal("UserName"));
                }
            }
        }
    }
}

关键注意事项

  • 白名单校验要覆盖所有动态表/列名,避免遗漏;复杂场景下可借助SQL语法解析库确保列名格式合法
  • 永远不要信任用户传入的任何SQL片段,即使声称仅包含AND/OR,也要确保所有变量值都通过参数传递
  • 避免使用EXEC或动态SQL执行方法,除非完全可控(如仅由内部代码生成,无用户输入参与)
  • 优先使用SqlParameter的强类型方法(如Add指定SqlDbType)代替AddWithValue,避免隐式类型转换问题

内容的提问来源于stack exchange,提问作者RunXin Shirley

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 14:43:21