1

I'm getting my filters as strings using the dynamic linq core library. Here I am sending a string expression to apiye. I am parsing my string expression to expression with the apide dynamic linq core library. What path should I follow for security here? As an example, I created a method named ValidatePredicate below. do you have any advice on this?

public class ValidatePredicate
{
    private static readonly Dictionary<Type, string[]> AllowedFieldsByType = new Dictionary<Type, string[]>
    {
        { typeof(OrganisationItem), new[] { nameof(OrganisationItem.ItemType), nameof(OrganisationItem.OrganisationItemName), nameof(OrganisationItem.isActive), nameof(OrganisationItem.UpdatedDate) } },
        // The fields that will be allowed for other entities should be written.        };
    public bool ValidatePredicateString<TEntity>(string predicate)
    {
        // entity'nin izin verilen alanlarini aliriz
        if (!AllowedFieldsByType.TryGetValue(typeof(TEntity), out var allowedFields))
        {
            return false; // returns false if it is a disallowed round          }

        // Allowed operators
        var allowedOperators = new[] { "==", "<=", ">=", "!=", "<", ">", ".Contains" };

        // 
        var parts = Regex.Split(predicate, @"&&|\|\|"); //'$$' to '||' in the predicate expression removes expressions

        foreach (var part in parts)
        {
            var trimmedPart = part.Trim(); //removes spaces in part
            bool validPart = false;

            //we control each operator.
            foreach (var op in allowedOperators)
            {
                if (op == ".Contains")
                {
                    var match = Regex.Match(trimmedPart, @"(\w+)\.Contains\(");
                    if (match.Success && allowedFields.Contains(match.Groups[1].Value)) //We get the name before the .contains statement.
                    {
                        validPart = true;
                        break;
                    }
                }
                else
                {
                    var segments = trimmedPart.Split(new[] { op }, StringSplitOptions.None);
                    var leftSegment = segments[0].Trim(); //The name to the left of the operator in the relevant expression is taken

                    if (allowedFields.Contains(leftSegment)) //check if the left side matches the allowed fields
                    {
                        validPart = true;
                        break;
                    }
                }
            }

            if (!validPart)
            {
                return false; // Returns false if a disallowed entity property name is found
            }
        }


        //check allowed operators
        //foreach (var op in allowedOperators)
        //{
        //  if (predicate.Contains(op))
        //  {
        //      continue;
        //  }

        //  return false; // Returns false if operator is not allowed
        //}

        //';' in expression returns false if
        if (predicate.Contains(";"))
        {
            return false;
        }

        return true;
    }

}
nikki
  • 25
  • 5
  • Parsing strings properly is quite hard, even if you do know all the details in advance. I would suggest against it. Instead, you should probably explore using Linq expressions. That would be much easier to avoid a lot of the potential pitfalls of string parsing. – Zohar Peled Aug 14 '23 at 07:38
  • I need to do dynamic filter and sorting. that's why I work with string expressions. How can I do this in linq expressions? – nikki Aug 14 '23 at 07:44
  • Are you using an ORM of some kind, or just plain old ADO.Net? – Zohar Peled Aug 14 '23 at 07:46
  • I'm using ef core. I am using dynamic linq core library for dynamic filter and sorting. On the client side, I create the predicate statement as a string and give it in dto in api controller. where predicate is `predicate = DynamicExpressionParser.ParseLambda(ParsingConfig.Default, false,pageRequest.Predicate);` I am parsing. – nikki Aug 14 '23 at 07:53
  • Does this answer your question? [How can I add user-supplied input to an SQL statement?](https://stackoverflow.com/questions/35163361/how-can-i-add-user-supplied-input-to-an-sql-statement) – Your Common Sense Aug 14 '23 at 10:31

1 Answers1

0

This still will allow executing quite a lot of arbitrary SQL on server (for example information disclosure, i.e. if you have predefined filter on customer malicious then user can do where customerId = companyId and /*required dynamic filters*/ or 1 = 1 ).

If you want controlled dynamical filtering and prevent injections - create corresponding DSL (for example something similar to specification pattern), parse it and build it back using query parameterization, so something like field1 = 1 or field2 = 'foo' will be parsed, validated that only allowed fields are used for comparisons and turned into field1 = @param1 or field2 = @param2 and values 1 and 'foo' added as parameters.

Also your code in no way enforces the claimed "// Return false if a disallowed property is found", it actually just checks that all items from allowedFields are present, the same goes for // Return false if a disallowed operator is found.

See also:

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • I wrote the current version of the code, is it an efficient way or should I look for another solution? i will use this on my listing pages, in a personnel management system. – nikki Aug 14 '23 at 07:56