在下面的示例代码中,表名是一个输入参数。在这种情况下,如何避免使用sp_executesql
进行SQL注入。下面是示例代码,我试图使用sp_executesql
来避免它,但它不起作用。有人能告诉我如何改正吗?
ALTER PROC Test @param1 NVARCHAR(50),
@param2 INT,
@tblname NVARCHAR(100)
AS
BEGIN
DECLARE @sql NVARCHAR(1000)
SET @sql= N' select * from ' + @tblname
+ ' where name= @param1 and id= @param2';
PRINT @sql
EXEC Sp_executesql
@sql,
N'@param1 nvarchar(50), @param2 int',
@param1,
@param2;
END
EXEC Test
'John',
2,
' emp; delete from emp where id = 567; select * from emp '
输出:打印消息:
select * from emp; delete from emp where id = 567; select * from emp where name= @param1 and id= @param2;
所有输入参数都被完全替换,并且一行被删除。请告诉我如何处理这种情况。
您可以首先检查参数值是否确实是表名:
ALTER PROC Test @param1 NVARCHAR(50),
@param2 INT,
@tblname NVARCHAR(100)
AS
BEGIN
DECLARE @sql NVARCHAR(1000)
IF EXISTS(SELECT 1 FROM sys.objects WHERE type = 'u' AND name = @tblname)
BEGIN
SET @sql= N' select * from ' + @tblname
+ ' where name= @param1 and id= @param2';
PRINT @sql
EXEC Sp_executesql
@sql,
N'@param1 nvarchar(50), @param2 int',
@param1,
@param2;
END
END
如果传递的值不是表名,则过程不会执行任何操作;或者您可以更改它以引发错误。这样,如果参数包含一个查询,您就安全了。
您可以将表名包含在[]
中
SET @sql= N' select * from [' + @tblname + '] where name= @param1 and id= @param2';
但是,如果使用由两部分组成的命名约定,例如dbo.tablename
,则必须添加额外的解析,因为[dbo.tablename]
将导致:
对象名称[dbo.tablename]无效。
您应该解析它,使它等于dbo.[tablename]
。