Playing around with Meteor, I have found that even with the insecure package removed, the client can change the Meteor.userId function. For example,
Meteor.userId=function() {return "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"}
as can be done with Meteor.default_connection.userId()
(the redirected function). How do I secure this?
Once a user is logged into your app with one of the methods described above, it is useful to be able to identify which user is logged in, and get the data provided during the registration process. For code that runs on the client, the global Meteor.userId () reactive function will give you the ID of the currently logged in user.
The Meteor Accounts system builds on top of the userId support in publish and methods. The core packages add the concept of user documents stored in the database, and additional packages add secure password authentication, integration with third party login services, and a pre-built user interface.
This package is the core of Meteor’s developer-facing user accounts functionality. This includes: A users collection with a standard schema, accessed through Meteor.users, and the client-side singletons Meteor.userId () and Meteor.user (), which represent the login state on the client.
Nor does Meteor know which user fields are needed by callbacks registered with Accounts.onLogin (), Accounts.onLogout (), Accounts.onLoginFailure () and Accounts.validateLoginAttempt (). To solve this problem Meteor 1.10 also introduced a new Accounts.config ( {defaultFieldSelector: {...}) option to include or omit specific user fields by default.
This is a great question because it shows how the Meteor security model works.
There's no security issue here because Meteor never trusts the client code.
In Meteor, only the server decides what data each client has access to (see Meteor.publish) and what data each client is allowed to change (see Meteor.allow). When a client authenticates to the server, the server stores the user's ID. Until that client logs out, it provides that ID to your Meteor.publish
and Meteor.allow
functions on the server as userId
.
Meteor also sends the user ID down on the client, because of course you want to change how the client behaves and what's on the screen based on who is logged in. And as you say, we can't stop a rogue client from arbitrarily changing any of its JavaScript code to change what it thinks the user ID is! But doing that doesn't give the client any new permissions, because it's still only the server code that makes the security decisions.
You can try this out using the secure parties application:
$ meteor create --example parties
Meteor.userId()
to get your user`s ID.Meteor.userId()
with a new function that returns the ID you want.So now you've faked the client to think that it's your user. But the server knows better. There still won't be a party on the screen, and you can't update the Parties collection to change that party information.
In fact, it's completely safe to set the client user ID to anything you want! You can reach right into the accounts system and call Meteor.default_connection.setUserId("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee");
. Try it, and you'll see that the login button in the upper right corner turns into an animation. That's because the client is calling Meteor.user()
to show the email address of the logged in user you just set. But because you haven't logged into the server as that user, it's not publishing any information about that user and you just get the spinny.
This is a very strong security model. You don't have to worry about any of the client code, even though in most apps that's where most of the code lives! As long as you write secure server methods, publish functions, and allow/deny rules, you're completely locked down no matter what the client tries to do.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With