无法使用FormattableString。

4

阅读这篇有趣的文章关于EF Core中SQL注入预防,我发现现在插值字符串可能会导致FormattableString

在.NET Core 2.2中运行此测试代码:

public static void Main()
{
    var filter = "Mark'; DROP TABLE tbl; --";

    Console.WriteLine(FromSql("SELECT * FROM tbl WHERE fld = '" + filter + "'"));
    Console.WriteLine(FromSql($"SELECT * FROM tbl WHERE fld = {filter}"));
    Console.WriteLine(FromSql(FormattableStringFactory.Create(
                                  "SELECT * FROM tbl WHERE fld = {0}", filter)));
}

private static string FromSql(string sql) => sql;

private static string FromSql(FormattableString sql)
{   
    var formatArgs = sql.GetArguments();

    for (var paramIndex = 0; paramIndex < sql.ArgumentCount; ++paramIndex)
        formatArgs[paramIndex] = "@p" + paramIndex;

    return sql.ToString();
}

不是我所期望的结果:

SELECT * FROM tbl WHERE fld = 'Mark'; DROP TABLE tbl; --'
SELECT * FROM tbl WHERE fld = Mark'; DROP TABLE tbl; --
SELECT * FROM tbl WHERE fld = @p0

第二个打印应该像最后一个一样输出。

试试这个 fiddle

我错过了什么吗?


我猜答案是:以$开头的字符串字面量实际上并没有构造一个FormattableString类型的字符串 - 我猜编译器还没有跟上库的步伐。 - 500 - Internal Server Error
@500-服务器内部错误 在链接的文章中,他们使用了确切的语法并且它有效。被调用的EF Core方法类似于我写的,你可以在这里看到:https://github.com/aspnet/EntityFrameworkCore/blob/915d214351f6c691f7fe7563bf7f9d51274845b0/src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs - Teejay
1个回答

3
你的第二个调用 Console.WriteLine(FromSql($"SELECT * FROM tbl WHERE fld = {filter}")); 看起来是调用了 FromSql(string sql) 重载方法而不是 FromSql(FormattableString sql)
只需移除 FromSql(string sql) 方法 (以及第一个调用)
它就会按预期工作;请参阅修改后的 Fiddle
编译器似乎将 var q = $"SELECT * FROM tbl WHERE fld = {filter}"; 翻译成了一个string
 String q = $"SELECT * FROM tbl WHERE fld = {filter}";`  

由于使用了 string 类型,发生了 String.Format
来自 文档 的说明:

如果插值字符串的类型为 string,它通常会被转换为一个 String.Format 方法调用。


如果明确指定FormattableString,则如下所示:

FormattableString q = $"SELECT * FROM tbl WHERE fld = {filter}";  

正在使用 FormattableStringFactory.Create
来自 文档:

如果插值字符串具有类型 IFormattable 或 FormattableString,编译器将生成对 FormattableStringFactory.Create 方法的调用。


有趣的是,我在上面的评论中链接的源代码中,另一个重载似乎使用了RawSqlString而不是string。这样可能编译器可以理解调用哪个重载函数。 - Teejay
1
谜底揭晓,它写在了“RawSqlString”文档页面上:“该类型可实现常规和内插FromSql()之间的重载解析”- https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.rawsqlstring - Teejay
有趣的是,现在他们将该方法标记为过时,并用两个明确的实现替换了它:FromSqlRaw 和 FromSqlInterpolated。 - Teejay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接