我知道动态SQL永远不应该是第一选择,并且相信我不是,但它现在正在运行,我想找到一种方法来至少提供某种针对SQL注入的保护,任何东西都是总比没有好吗?顺便说一句,我搜索了相关的问题并找到了一堆,但没有使用SQL Server上的SP。
在此应用程序中需要动态SQL的原因是由于程序可以执行多个存储过程,但是它们可以从不同的数据库中选择我们不知道它们将被命名的信息。我们知道这些数据库的名称将位于另一个数据库中,这就是用户以前的系统部署方式。
因此,当用户运行应用程序时,它会向他显示他可以访问的数据库,当他选择一个时,将使用所选数据库执行存储过程,这通过动态SQL执行。
起初我认为动态使用的唯一参数是数据库,但是看看参数是如何发送的,显然它们都是动态的,看看这个例子:
CREATE PROCEDURE testProc @myDatabase varchar(30), @myMonth varchar(10)
AS
BEGIN
DECLARE @sql nvarchar(1000)
SET @sql = 'SELECT * FROM '+@myDatabase+'.dbo.myTable WHERE Month='+@myMonth+''
EXECUTE sp_executesql @sql
END
现在,这个应用程序将主要用于那些最有可能不想放弃自己的数据库的人,并且他们将给出的选项将受到限制,但是应用程序允许他们创建更多字段来过滤数据,如果他们愿意,他们可以在这里或那里删除 drop
。我不是说他们愿意,但它可能会发生,那么有什么方法可以让这至少比程序创建本身更安全吗?
答案 0 :(得分:1)
您应该将sp_executesql
与参数一起使用,这是使用sp_executesql
而不是EXEC执行动态SQL的优势之一。此外,SQL Server 2008中数据库名称的最大长度为128个字符,因此您可能需要相应地更改@myDatabase
长度。我不确定是否可以在参数化命令字符串中插入数据库名称作为变量,但您可以在执行SELECT
语句之前检查此类数据库是否存在。
在你的情况下:
CREATE PROCEDURE testProc
@myDatabase NVARCHAR(30),
@myMonth VARCHAR(10)
AS
BEGIN
DECLARE @sql NVARCHAR(1000)
IF EXISTS (SELECT 1 FROM sys.databases AS db WHERE db.name = @myDatabase)
BEGIN
SET @sql = 'SELECT * FROM ' + @myDatabase + '.dbo.myTable WHERE Month=@myMonth';
EXECUTE sp_executesql @sql, N'@myMonth VARCHAR(10)', @myMonth
END
END
GO
当@myDatabase与sys.databases.name
进行比较时,它应该是NVARCHAR
,因为name
是SYSNAME
,类型为NVARCHAR(128)
。从用户获取参数的动态SQL是不安全的,但如果必须使用它,则应使用带参数的sp_executesql
过程。下面的书有一章描述了SQL注入和动态SQL的关系,这里有一些引用:
从T-SQL开发人员的角度来看,其中最重要的一个 方法是参数化动态SQL生成和执行 使用sp_executesql
查询Microsoft®SQLServer 2012,Itzik Ben-Gan,Dejan Sarka,Ron Talmage(第456页)。
参数化的能力意味着sp_excutesql避免了简单 类似于EXEC语句中使用的连接。结果,它 可以用来帮助防止SQL注入。
查询Microsoft®SQLServer 2012,Itzik Ben-Gan,Dejan Sarka,Ron Talmage(第458页)。