c# - 如何参数化 SQL 表而不会受到 SQL 注入(inject)的影响

标签 c# sql sql-server prepared-statement sql-injection

我正在编写一个 C# 类库,其中一个功能是能够创建与任何现有表的架构匹配的空数据表。

例如,这个:

private DataTable RetrieveEmptyDataTable(string tableName)
{
    var table = new DataTable() { TableName = tableName };

    using var command = new SqlCommand($"SELECT TOP 0 * FROM {tableName}", _connection);
    using SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
    dataAdapter.Fill(table);

    return table;
}

上面的代码可以运行,但是它有一个明显的安全漏洞:SQL 注入(inject)。

我的第一直觉是像这样参数化查询:

    using var command = new SqlCommand("SELECT TOP 0 * FROM @tableName", _connection);
    command.Parameters.AddWithValue("@tableName", tableName);

但这会导致以下异常:

Must declare the table variable "@tableName"

在 Stack Overflow 上快速搜索后,我找到了 this question ,建议使用我的第一种方法(具有 sqli 漏洞的方法)。那根本没有帮助,所以我继续搜索并找到了 this question ,这表示唯一安全的解决方案是对可能的表进行硬编码。同样,这不适用于我的类库,它需要为任意表名工作。

我的问题是:如何参数化表名而不会受到 SQL 注入(inject)的影响?

最佳答案

任意表名仍然必须存在,因此您可以先检查它是否存在:

IF EXISTS (SELECT 1 FROM sys.objects WHERE name = @TableName)
BEGIN
  ... do your thing ...
END

此外,如果您希望允许用户从中选择的表列表是已知的和有限的,或者匹配特定的命名约定(如 dbo.Sales% ),或者属于特定的模式(如 Reporting ) ,您可以添加额外的谓词来检查这些谓词。

这需要您将表名作为适当的参数传递,而不是连接或标记替换。 (还有 please don't use AddWithValue() for anything,永远。)

一旦检查对象真实有效,您仍然需要动态构建 SQL 查询,因为您仍然无法参数化表名。你还是应该申请QUOTENAME() , 不过,正如我在这些帖子中解释的那样:

所以最终的代码应该是这样的:

CREATE PROCEDURE dbo.SelectFromAnywhere
  @TableName sysname 
AS
BEGIN
  IF EXISTS (SELECT 1 FROM sys.objects
      WHERE name = @TableName)
  BEGIN
    DECLARE @sql nvarchar(max) = N'SELECT * 
      FROM ' + QUOTENAME(@TableName) + N';';
    EXEC sys.sp_executesql @sql;
  END
  ELSE
  BEGIN
    PRINT 'Nice try, robot.';
  END
END
GO

如果您还希望它在某个定义的列表中,您可以添加

AND @TableName IN (N't1', N't2', …)

LIKE <some pattern>或加入sys.schemas或者你有什么。

如果没有人有权修改程序以更改支票,则没有任何值可以传递给 @TableName这将允许你做任何恶意的事情,除了可能从另一个你没想到的表中选择,因为有太多访问权限的人能够在调用代码之前创建。替换像 -- 这样的字符或 ;不会让这更安全。

关于c# - 如何参数化 SQL 表而不会受到 SQL 注入(inject)的影响,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70013463/

相关文章:

c# - F# 中是否有类似于 Switch Case 命令的命令?

c# - 外键应该以哪种方式进入一对一关系

c# - 实现自己的 "Factory"以在 WPF 中重用 View

sql - 如何获取每年特定类别的计数?

sql - 数据库架构,数据库拆分

c# - SqlCommand 无法创建表

c# - "ambigous reference"上的错误两次指向同一方法

sql - 如何使用 hstore 进行 IN 查询?

python - SQLAlchemy:如何扩展混合属性?

mysql - SQL QUERY - UNION - 多个相同的表需要很长时间