【发布时间】:2016-05-06 16:16:17
【问题描述】:
我使用受this SO question 启发的存储过程添加了快速而简单的整个数据库搜索。它工作正常,但我担心 SQL 注入。当然我用的是 SQL 参数:
string query = GetUserInput();
using (SqlConnection con = new SqlConnection(conString))
{
using (SqlCommand cmd = new SqlCommand("SearchAllTables", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@SearchStr", SqlDbType.VarChar).Value = query;
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
但是,由于 SP 构建查询并执行它,SQL 注入仍然是可能的。相关部分:
CREATE PROC [dbo].[SearchAllTables]
(
@SearchStr nvarchar(100)
)
AS
BEGIN
CREATE TABLE #Results (TableName nvarchar(370), ColumnName nvarchar(370), ColumnValue nvarchar(3630), TableId int)
--snip boring part
SET @SearchStr2 = QUOTENAME('%' + @SearchStr + '%','''')
INSERT INTO #Results
EXEC
(
'SELECT ''' + @TableName +''', ''' + @TableName + '.' + @ColumnName + ''', LEFT(CONVERT(varchar(max), ' + @ColumnName + '), 3630), [id]
FROM ' + @TableName + ' (NOLOCK) ' +
' WHERE CONVERT(varchar(max), ' + @ColumnName + ') LIKE ' + @SearchStr2 --This is vulnerable
)
--snip all the END
SELECT TableName, ColumnName, ColumnValue, TableId FROM #Results
我想将其更改为使用带参数的 sp_executesql 以防止 SQL 注入。我把它改成:
declare @query nvarchar(1000)
set @query= 'SELECT ''' + @TableName +''', ''' + @TableName + '.' + @ColumnName + ''', LEFT(CONVERT(varchar(max), '
+ @ColumnName + '), 3630), [id] FROM ' + @TableName + ' (NOLOCK) ' +
' WHERE CONVERT(varchar(max), ' + @ColumnName + ') LIKE @term'
INSERT INTO #Results
EXEC sp_executesql @query, N'@term nvarchar(100)', @term=@SearchStr2;
但是我现在没有得到任何结果。我尝试在查询中包含“插入”并使用全局临时表但没有骰子。我可以做些什么来防止 SQL 注入并取回结果?还是我的方法有误?
【问题讨论】:
-
您为什么允许您的用户搜索系统中的任何表?为什么用户甚至知道表名?您可以将表名包含在 QUOTENAME 中以帮助防止 sql 注入,但老实说,整个概念对我来说似乎有点偏离。如果您要使用 NOLOCK,则需要包含 WITH 关键字。不推荐使用它。当然,如果您想在搜索中获得准确的结果,您应该使用该提示,因为它有时会丢失数据。
-
用户不提供表/列名,这些变量在程序的早期填充。用户的搜索词已被 QUOTENAME 包裹。
-
您的查询构建方式在我看来好像行不通,因为您使用 QUOTENAME 包装了搜索值。这用于对象名称,而不是值。你在底部编码的方式看起来是正确的,只需去掉你在提供的搜索词周围使用引号的部分。调试这种类型的动态 sql 的唯一方法是使用打印/选择语句。如果您查看 @query,您会看到您使用 quotename 引入的错误。
-
删除 QUOTENAME 有效,但我想了解错误。当我保留 QUOTENAME 并打印查询时,如果我搜索“test”,它会显示为
LIKE '%test%',这对我来说很好。 -
不仔细看。它现在在您的值周围有单引号,因为您使用了引号名称。而且您在动态 sql 中使用了一个参数,因此它正在寻找像 '%test%' 这样的值并且它不会找到任何东西,因为该单引号现在是搜索条件的一部分。
标签: c# sql-server