Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What logic must I cover in Collection.allow and Collection.deny to ensure it's secure?

Tags:

meteor

So just started playing with Meteor and trying to get my head around the security model. It seems there's two ways to modify data.

The Meteor.call way which seems pretty standard - pretty much just a call to the server with its own set of business rules implemented.

Then there is the Collection.allow method which seems much more different to anything I've done before. So it seems that if you put an collection.allow, you're saying that the client can make any write operation to that collection as long as it can get past the validations in its allow function.

That makes me feel uneasy cause it's feels like a lot of freedom and my allow function would need to be pretty long to make sure it's locked down securely enough.

For instance, mongodb has no schema, so you'd have to basically have a rule that defines which fields would be accepted and the format those fields must be in.

Wouldn't you also have to put in the business logic for every type of update that might be made to your system.

So say, I had a SoccerTeam collection. There may be several situations I may need to make a change, like if I'm adding or removing a player, changing the team name, team status has changed etc.

It seems to me that you'd have to put everything into this one massive function. It just sounds like a radical idea, but it seems Meteor.call methods would just be a lot simpler.

Am I thinking about this in the wrong manner (or for the wrong use case?) Does anyone have any example of how they can structure an allow or deny function with a list of what I may need to check in my allow function to make my collection secure?

like image 987
Diskdrive Avatar asked Feb 23 '14 13:02

Diskdrive


2 Answers

You are following the same line of reasoning I used in deciding how to handle data mutations when building Edthena. Out of the box, meteor provides you with the tools to make a simple tradeoff:

Do I trust the client and get a more responsive UI (latency compensation)? Or do I require strict control over data validation, but force the client to wait for an update?

I went with the latter, and exclusively used method calls for a few reasons:

  1. I sleep better a night knowing there exists exactly one way to update each of my collections.
  2. I found that some of my updates required side effects that only made sense to execute on the server (e.g. making denormalized updates to other collections).
  3. At present, there isn't a clear benefit to latency compensation for our app. We found the delay for most writes was inconsequential to the user experience.
  4. allow and deny rules are weak tools. They are essentially only good for validating ownership and other simple checks.

At the time when we first released to production (August 2013) this seemed like a radical conclusion. The meteor docs, the API, and the demos highlight the use of client-side writes, so I wasn't entirely sure I had made the right decision. A couple of months later I had my first opportunity to sit down with several of the meteor core devs - this is a summary of their reaction to my design choices:

This seems like a rational approach. Latency compensation is really useful in some contexts like mobile apps, and games, but may not be worth it for all web apps. It also makes for cool demos.

So there you have it. As of this writing, my advice for production apps would be to use client-side updates where you really need the speed, but you shouldn't feel like you are doing something wrong by making heavy use of methods.

As for the future, I'd imagine that post-1.0 we'll start to see things like built-in schema enforcement on both the client and server which will go a long way towards resolving my concerns. I see Collection2 as a significant first step in that direction, but I haven't tried it yet in any meaningful way.


stubs

A logical follow-up question is "Why not use stubs?". I spent some time investigating this but reached the conclusion that method stubbing wasn't useful to our project for the following reasons:

  1. I like to keep my server code on the server. Stubbing requires that I either ship all of my model code to the client or selectively repeat parts of it again. In a large app, I don't see that as practical.
  2. I found the the overhead required to separate out what may or may not run on the client to be a maintenance challenge.
  3. In order for the stub to do anything other than reject a database mutation, you'd need to have an allow rule in place - otherwise you'd end up with a lot of UI flicker (the client allows the write but the server immediately invalidates it). But having an allow rule defeats the whole point, because a user could still write to the db from the console.
like image 76
David Weldon Avatar answered Nov 15 '22 09:11

David Weldon


The usual allow methods I have are these:

MyCollection.allow({
    insert: false
    update: false
    remove: false
})

And then, I have methods which take care of all insertions. These methods perform the type checks and permission assessment. I have found that to be a much more maintainable method: completely decoupling the data layer from the code which runs on the client.


For instance, mongodb has no schema, so you'd have to basically have a rule that defines which fields would be accepted and the format those fields must be in.

Take a look at Collection2. They support schema checking at run-time before inserting documents into the Collection.

like image 36
musically_ut Avatar answered Nov 15 '22 08:11

musically_ut