【发布时间】:2015-11-17 22:28:33
【问题描述】:
我有一组对象,我希望用户在 LINQ 中编写自定义查询到对象。目前,我让用户在文本框中输入文本,例如
from t in tests where t.Name.EndsWith("st") select t
然后我将该文本传递给 LINQ“编译器”,它将该字符串作为输入并动态生成一个类。代码:
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CSharp;
namespace SecureLinqForUser
{
internal static class LinqCompiler
{
public static Type Compile(string linq)
{
var csc = new CSharpCodeProvider(new Dictionary<string, string> {{"CompilerVersion", "v3.5"}});
var parameters = new CompilerParameters(new[] {"mscorlib.dll", "System.Core.dll"}, "compiledlinq.dll", true)
{
GenerateExecutable = false,
GenerateInMemory = true
};
parameters.ReferencedAssemblies.Add(typeof (LinqCompiler).Assembly.Location);
parameters.CompilerOptions += " /platform:x64 ";
var results = csc.CompileAssemblyFromSource(parameters,
@"
using System.Linq;
using SecureLinqForUser;
using System.Collections.Generic;
class Linqed
{
public IEnumerable<Test> Query(Test[] tests)
{
IEnumerable<Test> list = " + linq + @";
return list;
}
}");
results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
return results.CompiledAssembly.GetType("Linqed");
}
}
}
使用给定的“编译器”,不受信任的用户可以输入类似的内容
new List<Test>();
// some malicious code here, not LINQ at all
因为没有检查输入的文本实际上是 LINQ。类似于 SQL 注入,我们称之为 LINQ 注入。
因此我主要关心的是让代码更安全。有没有例如一种预先解析文本以确保它只包含一个 LINQ 查询的方法?
出于 SSCCE 的目的,请同时查找其余代码:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace SecureLinqForUser
{
internal class Program
{
private static void Main()
{
Test[] tests =
{
new Test("Unit test"), new Test("System test"), new Test("Exploratory test"), new Test("Something"), new Test("Else")
};
var compile = LinqCompiler.Compile("from t in tests where t.Name.EndsWith(\"st\") select t;");
object obj = Activator.CreateInstance(compile);
var list = (IEnumerable<Test>) compile.InvokeMember("Query",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod,
null, obj, new[] {tests});
var sb = new StringBuilder();
foreach (var test in list)
{
sb.AppendLine(test.Name);
}
Console.WriteLine(sb.ToString());
Console.ReadLine();
}
}
public class Test
{
public string Name;
public Test(string v)
{
Name = v;
}
}
}
【问题讨论】:
-
你可以只使用 OData 吗?这样可以避免问题,但您可以限制他们查询的内容。
-
@johnny5:我处于早期开发阶段,所以是的,我基本上可以切换到完全不同的东西。到目前为止,我认为 OData 是基于网络的。我有一个独立的桌面应用程序。由于我的用户熟悉 SQL,因此查询语法不应离 SQL 太远。
-
您是否考虑过编写自己的自定义
QueryProvider?这样您就可以拒绝任何您不希望他们使用的结构。 -
@BradfordDillon:不是这样。当我看到 this list 我必须自己实现的事情时,我停止了研究。如果我理解正确,这意味着分别实现每个关键字(这些文章并不短)。
-
OData 可以在桌面应用程序中轻松使用,它具有与 SQL 类似的语法,但除此之外,您可以在 sql 中为它们创建一个用户,该用户只有某些特权,这至少有助于阻止恶意软件攻击
标签: c# linq linq-to-objects