1

It seems to me that breeze/odata queries present a significant risk for security access to data. For example, consider that I have an unrestricted entity (U) that is related to a restricted entity (R). I won't expose an endpoint to query for R and I'll write my client to query for U without including related Rs. But, a malicious client could request related Rs.

How do I prevent this?

I have a couple ideas. But, I have not been able to implement them yet to be able to say whether they work or not. None-the-less, here are my ideas:

1) Inspect each resulting entity -- after the query has been executed but before the result is sent to the client. But, I don't know how to insert my checking code (via callback or something) at the point between execution and sending to the client :(

2) Add smarts to the POCO to check for restricted entities and properties based on the user role. For example, instead of:

class MyThing{
  public string P {get;set;}
}

I'd have something like this:

private string _p;
public string P 
{ 
  get 
  { 
    if (UserRoles.HasAny("role-a","role-b"))
      return _p;
    return null; 
  }
  set { _p = value; }
}

That seems icky since a POCO is supposed to be dumb. The POCO would need to be able to read the user roles from somewhere ... maybe the HTTP session. I don't quite know how that will work.

I've read the following questions/answers, but they are not helping me: roles based security in breezejs and EF6, How is breeze.js handling security and avoiding exposing business logic, How to handle authorization with Breeze JS?

Community
  • 1
  • 1
steve
  • 1,021
  • 1
  • 14
  • 29

2 Answers2

0

You can use AllowedQueryOptions to prevent the client from performing $expand, as described in this SO answer.

Another way is to use the ODataQueryOptions as a parameter to your WebAPI controller method. This gives you the details of the query predicates in your server method, so that you can apply them as needed rather than letting WebAPI apply them automatically. This will let you expand, or not, based upon the query.

See this answer and this answer to see how it works.

Community
  • 1
  • 1
Steve Schmitt
  • 3,124
  • 12
  • 14
  • Preventing expand is too broad since in some cases expand is allowed into unrestricted tables but at the same time not into restricted tables. This sort of mechanism would work if I could list which tables for which expand is not allowed. As for your second idea: I'll try it! I do wonder whether the client code needs to change or whether the parameter will get loaded automagically. Also, I guess I can choose to either 1) inspect the odata options or 2) apply the odata options, execute the query and then check the query result. – steve Sep 16 '16 at 13:19
  • So, the options parameter does get loaded automatically. Cool! And it works ... sometimes. But sometimes I get this error: [[System.NotSupportedException: Unable to cast the type 'System.Web.Http.OData.Query.Expressions.SelectExpandBinder+SelectAllAndExpand`1[[DevList_Steve.Model.IssueTag, DevList-Steve, Version=1.0.6103.15380, Culture=neutral, PublicKeyToken=null]]' to type 'DevList_Steve.Model.IssueTag'. LINQ to Entities only supports casting EDM primitive or enumeration types.]] So, sadly that isn't going to work for me. – steve Sep 16 '16 at 20:07
0

Thanks to anyone who read my question, thought about it and even went so far as to respond. But, nothing suggested worked for me. So, I figured it out on by own. I can implement my option #1 my subclassing some breeze classes. I subclass EnableBreezeQueryAttribute so that I can override NewQueryHelper to return my QueryHelper subclass. Then, I use my CustomEnableBreezeQueryAttribute on my service methods. The ValidateData method is called with the entity objects. I could fail the method if it includes restricted information or I could blank out the restricted info -- allowing unrestricted info to be returned.

public class CustomEnableBreezeQueryAttribute : EnableBreezeQueryAttribute
{
    private class CustomQueryHelper : QueryHelper
    {
        public override IEnumerable PostExecuteQuery(IEnumerable queryResult)
        {
            queryResult = ValidateData(queryResult);
            return base.PostExecuteQuery(queryResult);
        }

        private IEnumerable ValidateData(IEnumerable queryResult)
        {
            //TODO: validate/modify data
        }
    }

    protected override QueryHelper NewQueryHelper()
    {
        return new CustomQueryHelper();
    }
}

I am surprised with how hard it was to figure out how to do this. And that it's not easier to inject code at this point. Leaves me with nagging questions: Am I doing something that should not be done? Or is this genuinely a hole in the functionality of odata/breeze? Or is this how things get done in odata/breeze?

steve
  • 1,021
  • 1
  • 14
  • 29