0

My question

Can I avoid expressions being routed through WebAPI input parameters? Is it even possible for that to happen?

My situation (context)

I have some code very similar to this (this is a simplification to make the point clearer):

// DTO Classes:

public abstract class FilterableDtoBase<TEntity>
{
    public string FilterValue { get; set; }
    public abstract Expression<Func<TEntity, bool>> FilterExpression { get; }
    public SortOrder SortOrder { get; set; } // Enum: SortOrder.Ascending, SortOrder.Descending
    public abstract Expression<Func<TEntity, string>> OrderExpression { get; }
}

public class InvoiceByInvoiceNumberFilterableDto: FilterableDtoBase<Invoice>
{
    public Expression<Func<Invoice, bool>> FilterExpression { get; } = i => i.InvoiceNumber.Contains(FilterValue);
    public Expression<Func<Invoice, string>> OrderExpression { get; } => i => i.InvoiceNumber;
}

// In a web API controller class:

[HttpGet]
[Route("invoices/")]
public async Task<Invoice> GetInvoices([FromUri] InvoiceByInvoiceNumberFilterableDto request)
{
    // Simulating EF: myDatamodel.Invoices is IQueryable<Invoice>
    var results = myDatamodel.Invoices.Where(request.FilterExpression);
    if (request.SortOrder == SortOrder.Ascending)
        results = results.SortBy(request.OrderExpression);
    else
        results = results.SortByDescending(request.OrderExpression);

    return results;
}

The objective behind this is two-fold:

  • have the logic be reusable enough so that I can keep adding these through my code base without repeating too much for similar behavior
  • allow consumers to have a consistent FilterValue=x&SortOrder=Ascending behavior.

However, as a result of this design, I realize that I'm passing lambdas that are constructed from requests DTOs directly into my model, the properties for the DTOs are exposed to Web API mapping as any other property.

The question:

Can an URI / Request body be constructed so that a consumer of the service can construct any lambda expression that they can use against me? I would like to know if this is something that WebAPI permits.

For example, could someone call http://example.com/invoices?FilterExpression=Expression.Call(MethodName=SQL,"DROP TABLE")..., or an expression in any way like this giving the attacker control over my code?

If so, can I prevent certain properties from being routed by Web API?

Update: My objective is not to allow the client to submit expressions. My goal is to make sure this cannot happen so that I'm not exposing myself to a security problem. The expressions are just used to abstract filters from the different entities in the system.

Alpha
  • 7,586
  • 8
  • 59
  • 92
  • C# Web API's come with built-in [Request Validation](https://msdn.microsoft.com/en-us/library/hh882339(v=vs.110).aspx) which will trigger a `Potentially dangerous request` Exception. If you are using the standard Web API solution/project. – Jake Jan 24 '17 at 22:03
  • 1
    How the heck are you getting parameters to bind to `Expression` objects?! – Jacob Jan 24 '17 at 22:03
  • I would be wary of completely open-ended expression request objects. Rather, I'd dynamically build an expression based on a fixed request object with something like LinqKit – David L Jan 24 '17 at 22:11
  • @Jacob I am not, but my question is exactly that: can it be done? (Which would translate to "am I in trouble?") – Alpha Jan 24 '17 at 23:36
  • @Jake it seems Request Validation may not cover these cases. See http://stackoverflow.com/a/2233249/147507 -- or am I missing something? – Alpha Jan 24 '17 at 23:40
  • I haven't used this, but OData was created to theoretically solve standardized query & filter functionality, and Web API supports it. More info here: https://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api – Jacob Jan 24 '17 at 23:41
  • @DavidL The DTOs are not that open ended. The expressions are not meant to be sent by the client, but rather to abstract behavior and obtain a filterable expression. – Alpha Jan 24 '17 at 23:43
  • I have submitted an update to the title and the description -- I think I wasn't clear enough because I'm actually trying to avoid what most are suggesting me how to achieve. The thing is, I want to know if it's possible. – Alpha Jan 24 '17 at 23:47
  • @Alpha Correct the exact string you mentioned probably wont be detected by out of the box Request Validation. You can [extend it](https://www.owasp.org/index.php/ASP.NET_Request_Validation#Extending_Request_Validation) though otherwise in your `GetInvoices` action manually sanitise `request` before using it. – Jake Jan 25 '17 at 00:07
  • @Alpha any time you expose an API, you have to assume it is open ended and subject to nasty behavior. – David L Jan 25 '17 at 00:21
  • @DavidL Agreed. Under the limits of the framework, which is what I'm trying to find out. My design needs to be improved, but I'm still curious about this case. – Alpha Jan 25 '17 at 03:00

1 Answers1

1

You probably should use an other design. Your Model FilterableDtoBase should only contains Fields you would expect from a request.

From the point of Security I don't see any problems. The process you mean with routing is actualy Model Binding.

We have some modelbinders in the default configuration. There is no ModelBinder for Expressions.

You could create a own Model Binder using the IModelBinder that could support something like that, but you shouldn't do it for obvious reasons.

Better would be a Descriptor DTO:

public abstract class FilterableDescriptor
{
    public string FilterValue { get; set; }
    public SortOrder SortOrder { get; set; } // Enum: SortOrder.Ascending, SortOrder.Descending
}

And an Extensionmethod something like this:

public static IQueryable ApplyFilter(this IQueryable query, FilterableDescriptor)
{
    //Your logic
    return query;
}

Then you could do something like this:

var results = myDatamodel.Invoices.Where(request.FilterExpression);
return results.ApplyFilter(request);
Christian Gollhardt
  • 16,510
  • 17
  • 74
  • 111