简单的问题,但也许没有简单的解决方案,至少我不能想到我的头脑之一,但那时我不是最好找到最好的解决方案。
我有一个存储过程,这个存储过程(以基本形式)在表上选择,设想这个:
SELECT * FROM myTable
好吧,很简单,除了它需要搜索的表名是不知道的,所以我们得到了一些非常类似的东西:
-- Just to give some context to the variables I'll be using
DECLARE @metaInfoID AS INT
SET @metaInfoID = 1
DECLARE @metaInfoTable AS VARCHAR(200)
SELECT @metaInfoTable = MetaInfoTableName FROM MetaInfos WHERE MetaInfoID = @MetaInfoID
DECLARE @sql AS VARCHAR(200)
SET @sql = 'SELECT * FROM ' + @metaInfoTable
EXEC @sql
所以,我认识到这最终是不好的,并且可以立即看到我可以执行sql注入攻击的地方。那么,问题是,有没有一种方法可以在不构建动态sql的情况下实现相同的结果?或者我是否必须超级,在我的客户代码中非常小心?
答案 0 :(得分:1)
如果您事先不知道表名,则必须使用动态sql。但是,您应该在尝试在SQL语句中使用它之前验证该值。
e.g。
IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=@metaInfoTable)
BEGIN
-- Execute the SELECT * FROM @metaInfoTable dynamic sql
END
这将确保存在具有该名称的表。当您查询INFORMATION_SCHEMA时,显然会有这样做的开销。您可以改为验证@metaInfoTable仅包含某些字符:
-- only run dynamic sql if table name value contains 0-9,a-z,A-Z, underscores or spaces (enclose table name in square brackets, in case it does contain spaces)
IF NOT @metaInfoTable LIKE '%^[0-9a-zA-Z_ ]%'
BEGIN
-- Execute the SELECT * FROM @metaInfoTable dynamic sql
END
答案 1 :(得分:0)
考虑到所描述的限制,我建议采用两种方式,性能与架构略有不同。
选择客户端&重新构建强>
我建议你应该尽可能考虑一个小的重新架构来强制调用者/客户端决定从哪个表中获取数据。将表名保存在另一个表中是code smell。
我在这里假设@MetaInfoID
正在从webapp,数据访问块等传递。这就是应该存放执行SELECT
的表的逻辑的位置。我会说客户端应该根据GetCustomers
知道要调用哪个存储过程(GetProducts
或@MetaInfoID
)。在您的DAL中创建新方法,如GetCustomersMetaInfo()
和GetProductsMetaInfo()
以及GetInvoicesMetaInfo()
,它们会调用相应的sprocs(不需要动态SQL,也不需要维护数据库中的元表)。 / p>
也许尝试重新构建一下系统。
在SQL Server中
如果您必须在数据库中执行此查找,并且根据您拥有的表的数量,您可以执行少量IF
语句(尽可能多的语句),如:
IF @MetaInfoID = 1
SELECT * FROM Customers
IF @MetaInfoID =2
SELECT * FROM Products
-- etc
这可能会成为维持的噩梦。
也许你可以为每个MetaInfo编写一个存储过程。通过这种方式,您可以获得预编译的优势,并且此处不会发生SQL注入。 (想象一下,如果有人破坏了MetaInfoTableName
列)
IF @MetaInfoID = 1
EXEC GetAllCustomers
IF @MetaInfoID = 2
EXEC GetAllProducts