C# 如何在sp_executesql中使用临时表和参数?
我使用一个存储过程添加了快速而肮脏的整个数据库搜索,该存储过程的灵感来自于。它工作正常,但我担心SQL注入。当然,我使用SQL参数:C# 如何在sp_executesql中使用临时表和参数?,c#,sql-server,C#,Sql Server,我使用一个存储过程添加了快速而肮脏的整个数据库搜索,该存储过程的灵感来自于。它工作正常,但我担心SQL注入。当然,我使用SQL参数: string query = GetUserInput(); using (SqlConnection con = new SqlConnection(conString)) { using (SqlCommand cmd = new SqlCommand("SearchAllTables", con)) {
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;
但是我现在没有得到任何结果。我尝试在查询中包含“insert-into”并使用全局临时表,但没有骰子。如何防止SQL注入并返回结果?或者我的方法错了吗?在动态查询中,诸如和列名之类的内容不能作为参数发送,因此必须附加它们。当然,这相当混乱(而且容易出错)
由于您使用的是C#,我建议您仔细研究一下。它提供了一些扩展,允许在LINQ查询中使用字符串查询。显示了生成动态查询的其他方法
好,回到你最初的问题
1) 动态Linq应允许您轻松编写查询,例如:
// this requires C# 6.0 to use interpolated strings. String.Format can be used instead
someRepository.GetAll.Where($"{col1} = {value1} And {col2} = {value2}");
因此,您有动态列和值,但需要动态表。一种方法是基于提供的类型动态获取存储库:
// this contains repositories for all types mapped to used tables
public class UnitOfWork : IUnitOfWork
{
public IRepository<Table1> Table1Repository { get; private set; }
public IRepository<Table2> Table2Repository { get; private set; }
// other come here
// all these are injected
public UnitOfWork(IDbContext context, IRepository<Table1> table1Repository, IRepository<Table2> table2Repository
{
Table1Repository = table1Repository;
Table2Repository = table2Repository;
// other initializations
}
// typed version
public IRepository<T> GetRepository<T>()
where T: class
{
Type thisType = this.GetType();
foreach (var prop in thisType.GetProperties())
{
var propType = prop.PropertyType;
if (!typeof(IRepository).IsAssignableFrom(propType))
continue;
var repoType = propType.GenericTypeArguments[0];
if (repoType == typeof(T))
return (IRepository<T>) prop.GetValue(this);
}
throw new ArgumentException(String.Format("No repository of type {0} found", typeof(T).FullName));
}
// dynamic type version (not tested, just written here)
public IRepository GetRepository(Type type)
where T: class
{
Type thisType = this.GetType();
foreach (var prop in thisType.GetProperties())
{
var propType = prop.PropertyType;
if (!typeof(IRepository).IsAssignableFrom(propType))
continue;
var repoType = propType.GenericTypeArguments[0];
if (repoType == type)
return (IRepository) prop.GetValue(this);
}
throw new ArgumentException(String.Format("No repository of type {0} found", typeof(T).FullName));
}
}
但是,如果要同时应用多个条件,这是相当棘手的
2) 一个有趣的方法是使用:
但是,我认为LINQ2SQL不能为Where生成SQL,因此源代码必须是对象列表。这是一个严重的问题,如果数据没有事先过滤以减少长度
3) 正如所指出的,表达式树似乎是最好的选择。比如:
var filterTableMap = new Dictionary<String, Type>()
{
{ "Table1", typeof(Table1Repository) },
{ "Table2", typeof(Table2Repository) },
// and so on
};
var param = Expression.Parameter(typeof(String));
var condition =
Expression.Lambda<Func<String, bool>>(
Expression.Equal(
Expression.Property(param, "Col1"),
Expression.Constant("Value1", typeof(String))
),
param
).Compile();
// for LINQ to SQL/Entities skip Compile() call
var query = source.Where(condition);
var param=Expression.Parameter(typeof(String));
var条件=
Lambda(
表达式。相等(
Expression.Property(param,“Col1”),
常量表达式(“值1”,类型(字符串))
),
param
).Compile();
//对于LINQ到SQL/实体,跳过Compile()调用
var query=source.Where(条件);
对于包含,解决方案更复杂,如图所示
我在C#中看到的对过滤建模的优势是:
// this requires C# 6.0 to use interpolated strings. String.Format can be used instead
someRepository.GetAll.Where($"{col1} = {value1} And {col2} = {value2}");
因此,您有动态列和值,但需要动态表。一种方法是基于提供的类型动态获取存储库:
// this contains repositories for all types mapped to used tables
public class UnitOfWork : IUnitOfWork
{
public IRepository<Table1> Table1Repository { get; private set; }
public IRepository<Table2> Table2Repository { get; private set; }
// other come here
// all these are injected
public UnitOfWork(IDbContext context, IRepository<Table1> table1Repository, IRepository<Table2> table2Repository
{
Table1Repository = table1Repository;
Table2Repository = table2Repository;
// other initializations
}
// typed version
public IRepository<T> GetRepository<T>()
where T: class
{
Type thisType = this.GetType();
foreach (var prop in thisType.GetProperties())
{
var propType = prop.PropertyType;
if (!typeof(IRepository).IsAssignableFrom(propType))
continue;
var repoType = propType.GenericTypeArguments[0];
if (repoType == typeof(T))
return (IRepository<T>) prop.GetValue(this);
}
throw new ArgumentException(String.Format("No repository of type {0} found", typeof(T).FullName));
}
// dynamic type version (not tested, just written here)
public IRepository GetRepository(Type type)
where T: class
{
Type thisType = this.GetType();
foreach (var prop in thisType.GetProperties())
{
var propType = prop.PropertyType;
if (!typeof(IRepository).IsAssignableFrom(propType))
continue;
var repoType = propType.GenericTypeArguments[0];
if (repoType == type)
return (IRepository) prop.GetValue(this);
}
throw new ArgumentException(String.Format("No repository of type {0} found", typeof(T).FullName));
}
}
但是,如果要同时应用多个条件,这是相当棘手的
2) 一个有趣的方法是使用:
但是,我认为LINQ2SQL不能为Where生成SQL,因此源代码必须是对象列表。这是一个严重的问题,如果数据没有事先过滤以减少长度
3) 正如所指出的,表达式树似乎是最好的选择。比如:
var filterTableMap = new Dictionary<String, Type>()
{
{ "Table1", typeof(Table1Repository) },
{ "Table2", typeof(Table2Repository) },
// and so on
};
var param = Expression.Parameter(typeof(String));
var condition =
Expression.Lambda<Func<String, bool>>(
Expression.Equal(
Expression.Property(param, "Col1"),
Expression.Constant("Value1", typeof(String))
),
param
).Compile();
// for LINQ to SQL/Entities skip Compile() call
var query = source.Where(condition);
var param=Expression.Parameter(typeof(String));
var条件=
Lambda(
表达式。相等(
Expression.Property(param,“Col1”),
常量表达式(“值1”,类型(字符串))
),
param
).Compile();
//对于LINQ到SQL/实体,跳过Compile()调用
var query=source.Where(条件);
对于包含,解决方案更复杂,如图所示
我在C#中看到的对过滤建模的优势是:
为什么允许用户搜索系统中的任何表?为什么用户甚至知道表名?您可以将表名包装在QUOTENAME中以帮助防止sql注入,但老实说,整个概念对我来说似乎有点不合适。如果要使用NOLOCK,则需要包含WITH关键字。不赞成省略它。当然,如果您希望在搜索中得到准确的结果,您应该使用该提示,因为它有时可能会丢失数据。用户不提供表/列名,这些变量在过程的前面已经填充。用户的搜索词已由QUOTENAME包装。在我看来,生成查询的方式似乎不起作用,因为您正在用QUOTENAME包装搜索值。用于对象名称而不是值的。在底部编码的方式看起来是正确的,只需去掉在提供的搜索词周围使用quotename的部分。调试这种类型的动态sql的唯一方法是使用print/select语句。如果您查看@query,您将看到您在quotename中引入的错误。删除quotename是有效的,但是我希望用户能够理解错误。当我保留QUOTENAME并打印查询时,如果我搜索“test”,它会显示为类似“%test%”的
,
,这在我看来很好。不要再仔细看了。由于使用了quotename,所以它现在在您的值周围有单引号。您正在动态sql中使用一个参数,因此它正在查找“%test%”之类的值,但找不到任何值,因为该单引号现在是搜索条件的一部分。为什么您允许用户搜索系统中的任何表?为什么用户