I went through this tutorial and I'm trying to figure out how authorization works:
https://www.openshift.com/blogs/day-15-meteor-building-a-web-app-from-scratch-in-meteor
I see that client-side code is well written and that it handles privileges for logged in users through calls to
if(Meteor.userId())
but as far as I can tell nothing is handling client authorization in the server-side code in epollserver.js.
I tried to create a script that likes questions in a for loop but failed :) So I'm wondering if I'm missing something about the blog, something about the way metamagics work in meteor or if the tutorial actually results with unsafe code?
Meteor has a built in accounts functionality, so it has a login package called accounts-base that handles this for you.
The code is in the background and not in your main project files. You can view the package's contents here: https://github.com/meteor/meteor/tree/devel/packages/accounts-base.
Meteor is slightly different in the way security is handled. The tutorial you're using doesn't mention it much, perhaps this is why the question comes about.
Its slightly mentioned with the insecure package. In meteor you can't just update fields as you please in the database without corresponding .allow rules (docs: http://docs.meteor.com/#allow).
If you keep the insecure package in, installed by default, you can edit collections as you please. This is why the tutorial recommends removing it.
Allow rules and Publish
You can specify a rule that tells meteor that only certain users are allowed to alter the database.
Likewise when it comes to autopublish, you can control what database data is sent down from the server to the client.
Combined, these two are what make meteor secure in the same way as conventional web applications. You have a browser which can see html and js, but when it comes to what data it can see or what data it can update it is controlled in a secure fashion.
On client-side, you can use Meteor.userId() to manage display fo logged user or anonymous.
In fact you can think it's not secure, but in fact it's enough, if you think well your server-side code. Let's me explain :
In meteor Js, you manage collection through publish/subscribe and direct network call through Meteor.call. So you have to secure both, on server-side.
For the first one you have to remove insecure and autopublish.
Then use allow/deny on your collections to manage who can insert/update/remove data.
Then in your publish functions, that are only on server-side, you have to add :
if (!this.userId) return this.error(new Meteor.Error(403, 'unauthorized access'));
For the second one (Meteor.call) you just have to add in all required Meteor.methods (on server-side) :
if (!Meteor.userId()) throw new Meteor.Error(403, 'unauthorized access');
With those thing your application is "secure". Yes a user can hack your client code to try to display templates that should be available only for logged user, but in fact, this user will not be able to retreive any data, so yes it is secure !
Also don't forget to split your server and client code in different folder (server and client) or all the code will be downloaded by the client. And this is not secure !
Hope it helps you understand Meteor Js.
Actually, the tutorial does result with unsafe code since I managed to add a question through the JS console without being logged in, my initial hunch that the code lacks server-side checks was correct
I suspect three more if(Meteor.userId()) in epollserver.js would fix the issue, it's a basic authorization coding failure of not observing the mantra "protect from the server first, then from the client if necessary"
Related
In my way learning Meteor the past weeks, trying to build a simple crud with some validation I stumbled upon something.
Until today, I've been trying to keep my Collection Schema on server's side, and had some difficulties rendering Schemas server validation on the client, so I added aldeed:autoform.
When I started playing with autoform, after a few errors, I was surprised to realize that a Collection's Schema HAD to be defined on client side in order to work properly.
Doesn't that seems a bit dangerous? Should the schema of your Collections be published on the client?
It is maybe a stupid question, im not sure. I'll apologize in advance :D
You'll need to define the schema so that it is accessible by both the client and the server code. This is because, the form validation is done once at the client side (which should be always treated as insecure as it's made available in the browser console like below) and once on the server side. The server side validation is always considered secure. So even if you manage to manipulate the form from the browser by whatever means, if the validation does not fall in line with the schema, it gets filtered out on the server side so long as the schema is attached to the collection. I believe this way it's secure.
Meteor autoform page suggests you to remove insecure and add allow/deny rules.
Be sure to define proper insert security for untrusted code if you've
removed the insecure package. Call allow/deny or use
ongoworks:security.
Steps I follow to keep things secure (please be aware that there may be better ways, since I'm also fairly new to meteor universe)
Remove autopublish and insecure.
Define schema with allow/deny rules.
Use meteor server methods to insert / update.
Whether you use the schema attached to a collection or a schema to just validate a form and not really want it to be attached to a collection, make sure you call [check()][4] on the server code so that the once the form is submitted at the client end to the server, the server further explicitly checks if the data is still in accordance with the schema and nothing has been injected / tampered with by someone or something untrusted. If it is manipulated, the server side validation automatically filters out the extra additions on to the data passed to it.
This is what happens if someone adds extra data into which does not exist / conform to the schema definition:
I'm not currently able to use all of Meteor's accounts functionality for compatibility reasons. My system is quite simple, though, I expose an "authenticate" method on the server that performs the necessary authentication work and then, if successful, sets the value of Meteor.userId() via this.setUserId. The only problem is that when I call this.setUserId on the server, it doesn't seem to propagate back to the client. Meter.userId() continues to return null on the client, but returns the correct value on the server. Since this.setUserId is a server-only function, I'm at a loss as to how I can set the correct user ID on the client after the "authenticate" method returns. Ideas?
You need to do more than just set the userId in order to authenticate a user. Check out these examples for how to do custom authentication:
https://github.com/tmeasday/meteor-accounts-anonymous (creates a user for each browser session)
https://github.com/mizzao/meteor-accounts-testing (creates users without passwords)
The second one is my example. I would strongly suggest building off the functionality provided by the base accounts package instead of rolling all your own operations.
EDIT: Based on the OP's response, one might be interested in doing a different kind of operation that is not about authenticating; see the following:
https://dweldon.silvrback.com/impersonating-a-user
Despite the fact that the Meteor documentation lists "this.setUserId()" as Server-only, it does currently work on the client too, so that's how I ended up solving this issue.
I was wondering if and how people are preventing data access on the client? Are people concerned with being able to perform insert/update/remove from the JS console with Meteor Apps?
I found this article describing how to limit database access on the client but its pretty old and not sure if it is still relevant. They describe this as a way to prevent the client from updating the database:
// Relies on underscore.js. In your project directory:
// $ meteor add underscore
Meteor.startup(function() {
var collections = ['collection_name_1', 'collection_name_2'];
_.each(collections, function(collection) {
_.each(['insert', 'update', 'remove'], function(method) {
Meteor.default_server.method_handlers['/' + collection + '/' + method] = function() {};
});
});
});
The Meteor accounts system has been extended since 0.5.0. It provides a collection.allow method that allows you to define limitations to access a collection. Check the docs.
The insecure package on Meteor allows all clients to edit any collection in the database. You need to ensure to meteor remove insecure and this will default to deny all clients to write to database.
The parties example screencast talks about this in detail.
I asked a similar question recently (What logic must I cover in Collection.allow and Collection.deny to ensure it's secure?)
I find Meteor's allow and deny collections to be woefully insecure. You're right, anyone can just edit the data they have available. And it's up to you to write your Allow function to properly check exactly how a user might manipulate a document.
At some stage, I'm sure Meteor will have built in features like schema checking or whatever to may it easier to do this in the future. Right now, I've concluded the easiest and cleanest thing I've found is to just revoke all server side database permissions on the client side and just do all write operations with Meteor.calls and do my own validations that way
I wanted to write a meteor app, that can post a tweet.
Since accounts-twitter goes through all the oAuth process and has all the data needed to make an authorized call to the Twitter API, I thought that's gonna be no problem.
As it turns out, it's a little trickier than that.
By default, accounts-twitter only exports the profile with the name of the logged in user. I augmented that to include the oAuth information - but in a stupid way: https://github.com/AVGP/meteor/commit/da29e812437c5e7b929599d8e2f4ff79279bfeb7
I am unhappy with this, because:
1.) It should not be in the "profile", but on the top-level (for which I need to touch the accounts-base/accounts-server.js, I guess.
2.) It should not be accessible on the client side (I guess), because that would allow stealing this info via XSS etc.
Can anybody give me some hint on how to implement that "properly"?
Thanks a lot!
Nevermind - I found it.
Actually, the trick is not to call Meteor.user() on the server side, but doing this instead:
Meteor.users.findOne(...).services.twitter
This gives you all the information and this stuff is hidden on Meteor.user().
If you turn autopublish off. How tamper proof and secure is the client side Meteor.users collection?
My experiments have shown it to be tamper proof. I have tried client side scripting on the console to insert, update and remove and get 403 errors.
For example you can try this, use one of your users collection ids and then $push something or $set something. You can also try remove and inserts too.
Meteor.users.update({_id: "72277b27-3a53-4aa3-82b7-fb096060a8dc"}, {$set: {foo:'bar'}})
or
Meteor.users.insert({this:'that'})
or
Meteor.users.remove({_id: "72277b27-3a53-4aa3-82b7-fb096060a8dc"})
and you should get a 403 on all of these.
But how tamper proof and secure is it?
Thanks
Steeve
Seeing as Meteor.users is fresh on the auth branch and not part of an official release, I'd consider it bleeding-edge with the possibility of bugs (which you should report if you find, by the way).
That being said, it appears to be designed with the goal of being tamper-proof and secure, given that it's part of an authentication system.