C# 为SELECT子句创建参数化字段名
我目前正在开发一个数据API,我想根据请求的字段名生成一个SQL查询。这些字段名将公开,用户还可以使用别名重命名API返回的数据 这些表中可用的记录不会作为“绝密”归档,但我仍然希望通过使用参数化的字段名和别名来防止任何SQL注入,以避免人们添加不需要的数据 声明@requestedFieldName NVARCHAR(max)=“FirstName” 声明@alias NVARCHAR(max)=“名字” 从MyTable中选择@requestedFieldName作为@alias 在WHERE子句和其他子句中有大量使用参数的示例,这些子句涉及要与字段匹配/分配/设置的值。。。但是,我在SQL server中找不到任何涉及参数化字段名/别名的示例(关于SQL server有一些问题,但没有)C# 为SELECT子句创建参数化字段名,c#,sql,parameters,sql-server-2014,parameterized,C#,Sql,Parameters,Sql Server 2014,Parameterized,我目前正在开发一个数据API,我想根据请求的字段名生成一个SQL查询。这些字段名将公开,用户还可以使用别名重命名API返回的数据 这些表中可用的记录不会作为“绝密”归档,但我仍然希望通过使用参数化的字段名和别名来防止任何SQL注入,以避免人们添加不需要的数据 声明@requestedFieldName NVARCHAR(max)=“FirstName” 声明@alias NVARCHAR(max)=“名字” 从MyTable中选择@requestedFieldName作为@alias 在WHER
是否有一种方法来参数化一个字段名,或者我应该考虑构建一个中间接口,它将保存一个用户可以请求的每个可用字段的列表吗?(我知道第二个选项经常使用,但我们有很多表,它们的结构将定期更改)。
使用varchar
参数获取列列表和表名。然后对照
sys.columns
表检查这些参数的值。如果所有列都匹配,则可以安全地使用这些值创建动态SQL并执行它。如果您还需要用户通过条件来构建where子句,请对其中的列执行相同的测试 更新 现在我已经创建了一个示例代码,我认为这可能不是适合您的最佳解决方案,除非您能够找到一种为每个表动态创建存储过程的方法。原因是where子句,而且每个表都有不同数据类型的不同列数,这使得where子句参数列表的创建非常依赖于表。(实际上,select参数数量的创建也取决于表格)。
因此,我不确定这是否是一个实用的解决方案,但这对我来说是一个有趣的t-sql挑战 尽管如此,我已经创建了代码示例,也许有人会找到使用这种过程的方法,因此我将与大家分享: 首先,示例表的DDL: 以及安全动态sql存储过程的示例: 当然,还有一个示例执行代码: 所有这些混乱的结果是:
您可以将每个表的列列表添加到c#代码中,并在其中构建动态查询。这可能是一个更短更快的代码。AFAIK,使用纯t-sql实现这一目标的唯一方法是使用动态sql。没有其他方法可以使用参数指定别名,也没有其他方法可以指定列名。也许可以对多个视图执行任何操作,让不同的用户访问不同的视图。即使转义字符,也可能由于不存在列而出错。解决这个问题的一个可能的方法是基于MsSQL列名标准构建一个正则表达式,并拒绝其他任何东西。可以根据
sys.columns
表轻松检查它们。但是,别名是你盔甲上的软肋。如何将数据返回到API客户端?数据将由web服务以JSON格式返回。别名可以在sql查询之后应用于JSON字符串。最初我为列列表编写了表值参数
,但后来我意识到这也会带来安全风险。
CREATE TABLE [dbo].[TblDays](
[Day_Id] [int] IDENTITY(1,1) NOT NULL,
[Day_Date] [date] NOT NULL,
[Day_Name] [nchar](10) NOT NULL,
CONSTRAINT [PK_TblDays] PRIMARY KEY CLUSTERED
(
[Day_Id] ASC
)
CREATE PROCEDURE DynamicalySelectFromTblDays
(
@ErrorMessage varchar(100) output,
@SelectCol1 sysname,
@SelectCol2 sysname = null,
@SelectCol3 sysname = null,
@WhereCol1 sysname = null,
@WhereCol2 sysname = null,
@WhereCol3 sysname = null,
@WhereValue1 int = null,
@WhereValue2 date = null,
@WherValue3 nchar(10) = null
)
AS
DECLARE @ExceptedColumnCount int,
@ActualColumnsCount int,
@TableName sysname = 'TblDays',
@SQL varchar(max)
-- get the number of columns expcected to get back from sys.columns
SELECT @ExceptedColumnCount = COUNT(*)
FROM (VALUES (@SelectCol1), (@SelectCol2), (@SelectCol3)) as x(a)
WHERE a is not null
-- get the number of columns from sys.columns
SELECT @ActualColumnsCount = COUNT(*)
FROM sys.columns c
INNER JOIN sys.tables t ON(c.object_id = t.object_id)
WHERE t.name = @TableName
AND c.name IN(@SelectCol1, @SelectCol2, @SelectCol3)
-- only if we get all of the non null columns back from the sys.columns table
IF @ExceptedColumnCount = @ActualColumnsCount AND (@ExceptedColumnCount = 0 OR @ActualColumnsCount > 0)
BEGIN
-- same test for where clause columns
SELECT @ExceptedColumnCount = COUNT(*)
FROM (VALUES (@WhereCol1), (@WhereCol2), (@WhereCol3)) as x(a)
WHERE a is not null
SELECT @ActualColumnsCount = COUNT(*)
FROM sys.columns c
INNER JOIN sys.tables t ON(c.object_id = t.object_id)
WHERE t.name = @TableName
AND c.name IN(@WhereCol1, @WhereCol2, @WhereCol3)
IF @ExceptedColumnCount = @ActualColumnsCount AND (@ExceptedColumnCount = 0 OR @ActualColumnsCount > 0)
BEGIN
-- dynamically build the sql statement:
SET @SQL = 'SELECT '+ COALESCE(@SelectCol1 +', ', '') + COALESCE(@SelectCol2 +', ', '')+ COALESCE(@SelectCol3, '') +
' FROM '+ @TableName
IF COALESCE(@WhereCol1, @WhereCol2, @WhereCol3) IS NOT NULL
BEGIN
SET @SQL = @SQL + ' WHERE '
IF @WhereCol1 IS NOT NULL
SET @SQL = @SQL + @WhereCol1 +' = '+ CAST(@WhereValue1 as varchar(10))
IF @WhereCol2 IS NOT NULL
SET @SQL = @SQL + ' AND ' + @WhereCol2 +' = '''+ CONVERT(char(10), @WhereValue2, 120) +''''
IF @WhereCol3 IS NOT NULL
SET @SQL = @SQL + ' AND ' + @WhereCol3 +' = '''+ @WherValue3 +''''
END
PRINT @SQL
-- EXEC(@SQL) -- Commented out since it's better to print first and look at the results, and only then execute it.
END
ELSE
BEGIN
-- perhaps using raise error instead would suit your needs better
SELECT @ErrorMessage = 'where columns does not match table columns'
END
END
ELSE
BEGIN
-- perhaps using raise error instead would suit your needs better
SELECT @ErrorMessage = 'select columns does not match table columns'
END
GO
DECLARE @ErrorMessage varchar(100),
@SelectCol1 sysname = 'Day_Id',
@SelectCol2 sysname = 'Day_Date',
@SelectCol3 sysname = 'Day_Name',
@WhereCol1 sysname = 'Day_Id',
@WhereCol2 sysname = 'Day_Date',
@WhereCol3 sysname = null,
@WhereValue1 int = 1,
@WhereValue2 date = convert(date, '14/04/2015', 103),
@WherValue3 nchar(10) = null
EXEC DynamicalySelectFromTblDays @ErrorMessage output,
@SelectCol1,
@SelectCol2,
@SelectCol3,
@WhereCol1,
@WhereCol2,
@WhereCol3,
@WhereValue1,
@WhereValue2,
@WherValue3
PRINT @ErrorMessage
SELECT Day_Id, Day_Date, Day_Name FROM TblDays WHERE Day_Id = 1 AND Day_Date = '2015-04-14'