Meteor Security


#1

I have recent come across Meteor and I think it looks really cool. I have questions about its security and the DB security. I’m new to the Node and Java Script scene and have the majority of my experience in the LAMP stack area. Can someone point me in the direction of the security of Meteor…


#2

http://security-resources.meteor.com/
https://meteorhacks.com/xss-and-meteor.html
https://meteorhacks.com/introducing-sikka-a-firewall-for-meteor-apps.html
http://docs.mongodb.org/manual/security/
http://docs.mongodb.org/manual/faq/developers/#javascript
https://atmospherejs.com/meteor/browser-policy
https://atmospherejs.com/ongoworks/security


#3

Hi Alan, welcome to the Meteor community!

Security can be a bit confusing with Meteor but to summarize it:

Trust nothing on the client. Assume they will change things like the ‘user role’ or ‘user id’ on the client. At first this sounds frightening but in reality it only means the worst case scenario is they see some admin template. The server will never send down (publish) the data because the server knows they are not an admin.

Any secure code should only run on the server. Concepts like stubs are fine because the ‘insert’ on the client will only simulate an insert. If it fails due to authorization (or malicious monkey patching on the client) they will never insert the document on the server because the check happens on the server.

The links that @spicemix posted should help as well!

Here’s a pattern that I use for model security that denies all clientside updates/inserts/removes and instead uses Meteor methods with security in the method. There’s nothing wrong with the allow/deny but I find this pattern helps make it easy to re-use and share across many apps/services.

Full file on Github


Meteor.methods({
  // returns {String} of document id
  "Secret.create": function(data) {

    if (User.isLoggedOut())
      throw new Meteor.Error(401, "You must be logged in");

    if (!User.isInRole('admin'))
      throw new Meteor.Error(401, "You must be an Admin");

    data.ownerId = this.userId;
    data.updatedAt = new Date();

    // throws error if signature is different
    check(data, {
      ownerId: String,
      updatedAt: Date,
      foo: String,
      bar: Match.Optional(Number)
    });

    var docId =  db.secrets.insert(data);
    console.log("  [Secret.create]", docId);
    return docId;
  }
  ...
});

Have fun!
Adam


#4

Don’t you mean

if (!this.userId)
  throw new Meteor.Error(401, "You must be logged in");

If you have your own function, I don’t think you mean to have the ! in front of the User.isLoggedOut().


#5

Security is totally different with Meteor.

You might find this thread interesting.

We found this application security guideline document from box very useful.

Albert


#6

Thanks for the link to https://atmospherejs.com/meteor/oauth-encryption

Some notes I’ve assembled so far:

  1. I think it’s good to have security run on both client and server, as a validation step. It doesn’t need to run on the client (but must run on the server of course), but if it’s present on the client the user and developer will know immediately if they’ve done something disallowed. Arguing we should use “security by obscurity” by never sending security code to the client is weaksauce. Better to let everyone know how your security works and make sure they have no way around it.

  2. The default state of a security system should be “No access no way.” I noticed a package (I’ll leave it nameless since no one uses it anyway) where if you have a secret key defined, you need authorization to access the server, but if there’s no secret key defined, everyone can access the server freely! No. If there’s no secret key defined, your server doesn’t serve, period. That’s a more secure implementation.

  3. Speaking of secret keys, meteor.com hosting via meteor deploy currently doesn’t take environment variables (which you can access in your code via process.env.MY_VARIABLE) so they tell you to use Meteor settings files in JSON instead. But you don’t want your secret deployment keys in Git. I don’t use meteor.com hosting and do use environment variables instead. You can work some way of securely managing the settings JSON instead if you like.

  4. Every time I learn something about security it goes into my current project. Don’t put it off to some productization phase. Start secure and get more secure as you develop. As I just did with OAuth token security. And yesterday did with https://atmospherejs.com/meteor/audit-argument-checks I am not even vaguely in the range of productization yet. Not just test-first development, security-first development. Because security is a universal acceptance criterion and should be part of every implementation step.

  5. Mongo security is a whole nother topic. Make sure anything that’s supposed to be a string is check(myVar, String) to ensure it’s not actually an object Mongo will interpret. This is one of the hazards of loosely typed JavaScript. You may also want to sanitize all input to Mongo so that it doesn’t get interpreted in aggregation or somewhere on the way out of the database as e.g. XSS.


#7

Doh! good catch, sorry about the confusing inversion :smiley:

I normally use User.isLoggedOut() because it’s a bit easier to read than !Meteor.userId() although you’re right !this.userId would work as well. (the former is a bit easier to stub in tests though).


#8

Thanks for the answers, they have been very useful to me to get a global idea about meteor security. However, I would like to ask one thing that seems very confusing to me. Could it be possible to call a meteor method with a different userId after been correctly logged?

Lets say i log into the webpage with a valid user xxx: password :zzz and I have a valid userId:yyyyy,
what happens if I maliciously patch the client to call the Meteor methods with a different userId:aaaa
(probably with bruteforce values) to see if I catch some results? Is that possible? Thanks in advance


#9

The userId on the client is only used for latency compensation. The server knows which client is logged in and the userId on the server can’t be changed from the clientside. They would have to actually log in as the 2nd user to call on their behalf. You can change the userId on the server if you’d like but in general you wouldn’t want to do that.


#10

Thanks for the answer, i just found this post where there’s also a clear explanation (also about the importance of keeping the tokenId safe)


#11

Great find!

Another security thing I do (unrelated to userId specifically) is to deny access to certain meteor methods that are server to server only (I use the micro-service pattern a lot). You can require a param with a secret token that is stored in Meteor.settings on the server.

Meteor.methods({
  getSecret: function(opts, token) {
    if (token !== Meteor.settings.rpc_token) throw new Meteor.Error(401);
  }
});