【问题标题】:SQL Server – pulling user data using dynamic query in stored procedureSQL Server – 在存储过程中使用动态查询拉取用户数据
【发布时间】:2019-03-17 22:03:52
【问题描述】:

我有问题,我无法使用存储过程查看用户信息。该过程接受三个参数:tablecolumnsearchBySomething。每次我想使用另一列搜索用户时,列变量接收 id 的列,searchBySomething 变量接收特定的 id,该过程有效,但是当我发送另一列时出现错误留言

无效的列名(数据)

过程如下:

ALTER PROCEDURE [dbo].[userDetailsDisplay] 
    @table NVARCHAR(30),
    @column NVARCHAR(30),
    @searchBySomething NVARCHAR(30)

    DECLARE @sql NVARCHAR(100)

    SET @sql = 'SELECT * FROM ' + @table + ' WHERE ' + @column + ' = ' + @searchBySomething 
    EXECUTE sp_executesql @sql

【问题讨论】:

  • 错误很明显 - 您要搜索的列在您尝试搜索的表中不存在。您为参数传递的确切值是什么?
  • 另外,如果您传入的列名中有空格(或其他一些值,例如连字符),您需要分隔列名。对表名和列名使用 QUOTENAME() 函数(这也提高了安全性),并且至少用两个单引号替换 @searchBySomething 中的任何单引号,例如REPLACE(@searchBySomething, '''', '''''') 帮助防止简单的注入攻击。
  • print @sql 看看发生了什么。
  • 这段代码在各种级别上都很糟糕。阅读 Xedni 的答案并接受它。
  • @squillman 如果实际的列名是the data,如错误消息所示,那么问题不在于该列不存在。问题是试图解析... WHERE the column = ...。正如 Laughing Virgil 指出的那样,QuoteName 至少应该在解析阶段取得更多成功。 (特别向Little Bobby Tables 致敬!)

标签: c# sql-server tsql dynamic-sql


【解决方案1】:

因此,您遇到的具体错误是因为您没有检查输入以查看传递给 @column 的字符串是否确实存在。您可以通过元数据目录视图sys.columns 来检查它是否存在,执行如下操作:

if not exists
(
    select 1
    from sys.columns
    where object_id = object_id(@table)
        and name = @column
)
begin
    raiserror('Column %s does not exist in table %t', 16, 1, @column, @table)
    return
end

但是,如果我不指出两件事,我将失职。

首先,这种动态表动态 where 子句模式是非常糟糕的做法。如果它是针对已经拥有数据库访问权限的人,他们可以简单地自己查询表。如果它是针对外部用户的,那么您基本上已经通过此过程为他们提供了完整的数据库读取访问权限。当然,在某些罕见情况下需要这种模式,所以如果你对使用动态 sql 很执着,那我就进入下一个观点。

您编写的代码容易受到 SQL 注入攻击。每当您使用动态 SQL 时,您必须非常小心它的构造方式。假设我传入了列名; drop database [admin]-- 假设你有这样一个数据库,我的可能会很高兴地被执行并且你的数据库会消失。

如何使动态 SQL 安全是一个复杂的话题,但如果您认真地想了解更多关于它的知识,这可能是您能找到的最好的文章之一。 http://www.sommarskog.se/dynamic_sql.html

通过参数化您的查询并在表和列上使用quotename(),我将其修改为如下所示。如果有人试图进行注入攻击,这仍然会抛出奇怪的错误,但至少它不会真正执行他们的代码。

create procedure [dbo].[userDetailsDisplay] 
    @table nvarchar(30),
    @column nvarchar(30),
    @searchBySomething nvarchar(30)
as
begin
    declare 
        @sql nvarchar(max),
        @params nvarchar(1000)

    if not exists
    (
        select 1
        from sys.columns
        where object_id = object_id(@table)
            and name = @column
    )
    begin
        raiserror('Column %s does not exist in table %t', 16, 1, @column, @table)
        return
    end

    select @sql = '
        select * 
        from ' + quotename(@table) + ' WHERE ' + quotename(@column) + ' = @searchBySomething'

    execute sp_executesql 
        @stmt = @sql,
        @params = '@searchBySomething nvarchar(30)',
        @searchBySomething = @searchBySomething
end

【讨论】:

  • 您好,谢谢,我复制了代码,但仍然是同样的错误
  • 那是因为我明确地扔了一个。如果您传递了一个无效列(如果不是错误),您希望它做什么?恕我直言,这个错误是合法的,这只是说明了原因,以及如何检查它的行为是否会有所不同。
  • 只是一个很好的答案的旁注 - 我知道这来自 OP 的原始代码 - @table@column 应该是 sysname 类型,而不是 nvarchar(30)
【解决方案2】:

SET @sql = 'SELECT * FROM ' + @table + ' WHERE ' + @column + ' = ' +''' @searchBySomething +''''

例如:select * from table where column ='value'

【讨论】:

    【解决方案3】:

    只需检查以确保该列存在于表中。

    对于每个调用的@table,检查@column 变量是否在该表中。

    【讨论】:

      猜你喜欢
      • 2022-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多