1
votes

I'm using BreezeJS and have a question regarding how data is saved. Here's my code and comments

    [Authorize]
    /*
     * I want to point out the security hole here.  Any Authorized user is able to pass to this method
     * a saveBundle which will be saved to the DB.  This saveBundle can contain anything, for any user,
     * or any table. 
     * 
     * This cannot be stopped at the client level as this method can be called from Postman, curl, or whatever.
     * 
     * The only way I can see to subvert this attack would be to examine the saveBundle and verify
     * no data is being impacted that is not owned or related directly to the calling user.
     * 
     * Brute force could be applied here because SaveResult contains Errors and impacted Entities.
     * 
     */

    [HttpPost]
    public SaveResult SaveChanges(JObject saveBundle)
    {
        return _efContext.SaveChanges(saveBundle);
    }

To limit access to a callers ability to retrieve data I first extract from the access_token the user_id and limit all my queries to include this in a where clause, making it somewhat impossible for a user to retrieve another users data.

But that would not stop a rogue user who had a valid access_token from calling SaveChanges() in a brute force loop with incremental object ids.

Am I way off on this one? Maybe I'm missing something.

Thanks for any help.

Mike

1
Not familiar with breeze, but you sound like you are implementing proper access control. It's also good practice to use cryptographic randomness in generating your object ids, but really that is secondary to having proper access control implemented. In terms of SaveChanges(), you may want to limit the number of save operations the user can do -- otherwise he can fill up your db with junk.TheGreatContini

1 Answers

3
votes

The JObject saveBundle that the client passes to the SaveChanges method is opaque and hard to use. The Breeze ContextProvider converts that to a map of entities and passes it to the BeforeSaveEntities method. BeforeSaveEntities is a method you would implement on your ContextProvider subclass, or in a delegate that you attach to the ContextProvider, e.g.:

  var cp = new MyContextProvider();
  cp.BeforeSaveEntitiesDelegate += MySaveValidator;

In your BeforeSaveEntities or delegate method, you would check to see if the entities can be saved by the current user. If you find an entity that shouldn't be saved, you can either remove it from the change set, or throw an error and abort the save:

protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(
                Dictionary<Type, List<EntityInfo>> saveMap)
{
  var user = GetCurrentUser();
  var entityErrors = new List<EFEntityError>();
  foreach (Type type in saveMap.Keys)
  {
    foreach (EntityInfo entityInfo in saveMap[type])
    {
      if (!UserCanSave(entityInfo, user))
      {
        throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Forbidden)
          { ReasonPhrase = "Not authorized to make these changes" });
      }
    }
  }
  return saveMap;
}

You will need to determine whether the user should be allowed to save a particular entity. This could be based on the role of the user and/or some other attribute, e.g. users in the Sales role can only save Client records that belong to their own SalesRegion.