c# - Entity Framework Core - .Contains() - 为什么转义而不是参数化?

标签 c# sql linq asp.net-core entity-framework-core

在 Web 应用程序中,我以 List<string> 的形式获取用户输入来自 ViewModel 并使用此信息通过以下代码选择用户的 ID:

var selectedUsersIds = Context.Users
.Where(user => SelectedUsers.Contains(user.Email))
.Select(user => user.Id)
.ToList();

SelectedUsers是字符串列表(用户电子邮件)。

现在,在查看应用程序日志时,我遇到了以下日志条目:

info: Microsoft.Data.Entity.Storage.Internal.RelationalCommandBuilderFactory[1]
  Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
  SELECT [user].[Id]
  FROM [AspNetUsers] AS [user]
  WHERE [user].[Email] IN ('first@user.com', 'second@user.com')

所以下一个任务是使用休息客户端并使用 some@user'--作为表单参数,我得到了这个结果:

info: Microsoft.Data.Entity.Storage.Internal.RelationalCommandBuilderFactory[1]
  Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
  SELECT [user].[Id]
  FROM [AspNetUsers] AS [user]
  WHERE [user].[Email] IN ('some@user''--')

在这里,single '逃到了double '' .这种行为似乎与 what is described in the docs under "Security Guarantee: LINQ queries use parameterization and escaping" ,它声明查询将被参数化或转义。

但是,我想知道,如何决定何时对查询进行参数化以及何时对值进行转义?选择其中任何一个的理由是什么? 而且我认为逃跑从来都不是 100% 安全的,现在有什么不同吗?

最佳答案

好问题。我相信答案是如果元素可以转换为常量并且它不是 UPDATE 或 INSERT,它将被转义。原因如下:

查看 SqlGenerator Souce显示一个名为 GenerateSql 的方法看起来像这样:

internal static string GenerateSql(DbCommandTree tree, SqlVersion sqlVersion, out List<SqlParameter> parameters, out CommandType commandType, out HashSet<string> paramsToForceNonUnicode)
{
    SqlGenerator sqlGen;
    commandType = CommandType.Text;
    parameters = null;
    paramsToForceNonUnicode = null;

    switch (tree.CommandTreeKind)
    {
        case DbCommandTreeKind.Query:
            sqlGen = new SqlGenerator(sqlVersion);
            return sqlGen.GenerateSql((DbQueryCommandTree)tree, out paramsToForceNonUnicode);

        case DbCommandTreeKind.Insert:
            return DmlSqlGenerator.GenerateInsertSql((DbInsertCommandTree)tree, sqlVersion, out parameters);

        case DbCommandTreeKind.Delete:
            return DmlSqlGenerator.GenerateDeleteSql((DbDeleteCommandTree)tree, sqlVersion, out parameters);

        case DbCommandTreeKind.Update:
            return DmlSqlGenerator.GenerateUpdateSql((DbUpdateCommandTree)tree, sqlVersion, out parameters);

        case DbCommandTreeKind.Function:
            sqlGen = new SqlGenerator(sqlVersion);
            return GenerateFunctionSql((DbFunctionCommandTree)tree, out commandType);

        default:
            //We have covered all command tree kinds
            Debug.Assert(false, "Unknown command tree kind");
            parameters = null;
            return null;
    }
}

如您所见,如果它是一个查询,它会返回生成的不带参数的 SQL。对于其他种类,它将填充 List<SqlParameter> .

是否会处理一个常量,我们可以在同一个类的其他地方看:

有评论here上面写着:

// Constants will be sent to the store as part of the generated TSQL, not as parameters

我们已经确定对于 INSERT 或 UPDATE 或 DELETE,它将使用参数。所以,这是为了查询。你的List<string>正如您从日志中看到的那样,当它传入时被转换为常量。所以,我们只想知道这些字符串会发生什么。

然后类型发生了很大的变化,相关的部分是

case PrimitiveTypeKind.String:
    bool isUnicode;

    if (!TypeHelpers.TryGetIsUnicode(e.ResultType, out isUnicode))
    {
        // If the unicode facet is not specified, if needed force non-unicode, otherwise default to unicode.
        isUnicode = !_forceNonUnicode;
    }
    result.Append(EscapeSingleQuote(e.Value as string, isUnicode));
    break;

哪个做的简单

private static string EscapeSingleQuote(string s, bool isUnicode)
{
    return (isUnicode ? "N'" : "'") + s.Replace("'", "''") + "'";
}

关于你的其他问题

What reasons are there to chose either one? And I thought that escaping was never 100% safe, is this different now?

虽然我不是安全专家,但我倾向于同意“转义不能 100% 安全”,并且有人可能会向您指出一些有限范围、100% 安全的方法。只有当我 100% 确定不会发生任何有趣的事情时,我才会选择转义:也就是说,当值不可能直接来自用户时。因此,您可能希望对您的实现进行更多测试,并根据访问限制、风险承受能力、数据敏感性和用户角色等其他因素来决定是否需要进一步保护它。

关于c# - Entity Framework Core - .Contains() - 为什么转义而不是参数化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35484383/

相关文章:

c# - 带有 gecko 浏览器的 Multi sock5?

c# - 如何将 SAme 类的多个对象序列化为单个文件并在 C# 中反序列化为这些对象

c# - 2001-01-01 00 :00:00. 000 改为插入数据库 2000-12-31 23:59:59

php - 如何创建多词搜索?数据库

sql - FORMAT 函数在 sql server 2008 R2 中不起作用

c# - 常量字段或获取属性

sql - 查询一个表,将分组行中的某些列映射到同一行中的多个列

c# - 解析 Linq.Expressions.NewExpression?

linq - 如何使用 LINQ 订购一个集合及其子集合?

c# - LINQ-to-objects 中的 Enumerable.Where 是否保留顺序?