LINQ to Entities中Where子句中的引号转义

时间:2013-06-01 14:04:24

标签: c# linq entity-framework escaping sql-injection

我想知道如何逃避LINQ to Entities中的引号。

这是我的环境:使用Silverlight 5和WCF RIA服务的实体框架5,MySQL 5.6和MySQLConnector 6.5.6。

我有以下查询:

DomainContext.Load<Product>(DomainContext.GetProductQuery()
                                         .Where<Product>(p => p.name.Contains(parameter))
                                         .Take<Product>(30));

如果参数变量包含引用',则会引发MySQL语法错误异常。 无论方法(StartWith,Contains)如何,它总是会引发异常。

使用带有 DomainDataSource FilterDescriptor 也是如此。

重要提示:它不会引发%或双引号"等字符的异常。如果运算符等于严格,它也不会通过简单引用引发任何异常,如下所示。

DomainDataSource.FilterDescriptors.Add(new FilterDescriptor("productName", FilterOperator.IsEqualTo, SelectedProductName));

DomainContext.Load<Product>(DomainContext.GetProductQuery()
                                         .Where<Product>(p == parameter)
                                         .Take<Product>(30));

插入数据没有任何困难。

任何帮助将不胜感激。 谢谢。

更新:我忘了提一些事情。

这是我在服务方面的方法。

public IQueryable<Product> GetProduct()
{
    return this.ObjectContext.product;
}

我应该如何针对SQL注入确保这一点?我是否必须编写几十行代码来管理过滤器?

编辑:问题在EF的MySQL提供程序的最新版本中得到解决。

2 个答案:

答案 0 :(得分:0)

快速解决您的确切问题:

string cleanParameter = parameter.Replace("'", "\'")

OR

查看here以获得更通用的解决方案,该解决方案描述了与mysql_real_escape_string等效的C#。

string cleanParameter = MySQLEscape(parameter)

MySQLEscape,如上述文章中所述:

private static string MySQLEscape(string str)
{
    return Regex.Replace(str, @"[\x00'""\b\n\r\t\cZ\\%_]",
        delegate(Match match)
        {
            string v = match.Value;
            switch (v)
            {
                case "\x00":            // ASCII NUL (0x00) character
                    return "\\0";   
                case "\b":              // BACKSPACE character
                    return "\\b";
                case "\n":              // NEWLINE (linefeed) character
                    return "\\n";
                case "\r":              // CARRIAGE RETURN character
                    return "\\r";
                case "\t":              // TAB
                    return "\\t";
                case "\u001A":          // Ctrl-Z
                    return "\\Z";
                default:
                    return "\\" + v;
            }
        });
}

附注:您的代码听起来可能容易发生 SQL注入攻击。 (没有更多背景,我无法分辨)。这个article描述了sql注入攻击是什么以及如何防止它们。

答案 1 :(得分:0)

我找到了解决此问题的方法。

首先,我要感谢来自www.developpez.net的 tomlev ,因为他的解决方案也是 chamamo

这是法语讨论的直接链接 http://www.developpez.net/forums/d1349604/services-web/wcf-ria-services-injection-sql/

这是解决此问题的包装器的源代码。

    class MySqlQueryableWrapper<T> : IQueryable<T>
{
    private readonly IQueryable<T> _queryable;
    private readonly IQueryProvider _provider;

    public MySqlQueryableWrapper(IQueryable<T> queryable)
    {
        _queryable = queryable;
        _provider = new MySqlQueryProviderWrapper(queryable.Provider);
    }

    public Type ElementType
    {
        get { return _queryable.ElementType; }
    }

    public Expression Expression
    {
        get { return _queryable.Expression; }
    }

    public IQueryProvider Provider
    {
        get { return _provider; }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _queryable.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

class MySqlQueryProviderWrapper : IQueryProvider
{
    private readonly MySqlExpressionFixer _visitor = new MySqlExpressionFixer();
    private readonly IQueryProvider _provider;

    public MySqlQueryProviderWrapper(IQueryProvider provider)
    {
        _provider = provider;
    }

    public IQueryable CreateQuery(Expression expression)
    {
        return _provider.CreateQuery(_visitor.Visit(expression));
    }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    {
        return _provider.CreateQuery<TElement>(_visitor.Visit(expression));
    }

    public object Execute(Expression expression)
    {
        return _provider.Execute(_visitor.Visit(expression));
    }

    public TResult Execute<TResult>(Expression expression)
    {
        return _provider.Execute<TResult>(_visitor.Visit(expression));
    }

}

class MySqlExpressionFixer : ExpressionVisitor
{    
    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        if ((node.Method.Name == "Contains" || node.Method.Name == "StartsWith") &&
            node.Method.DeclaringType == typeof(string) &&
            node.Arguments.Count == 1)
        {
            var c = node.Arguments[0] as ConstantExpression;
            if (c != null)
            {
                string s = c.Value as string;
                if (s != null)
                {
                    s = s.Replace("'", "''");
                    node = Expression.Call(node.Object, node.Method, Expression.Constant(s));
                }
            }
        }

        return base.VisitMethodCall(node);
    }
}

这是一个例子。

public IQueryable<Product> GetProduct()
{
    return new MySqlQueryableWrapper<Product>(this.ObjectContext.product);
}