Meteor security fundamentals

Philipp Muens
6 min readAug 26, 2015

Let’s talk about Meteor and security.

There are several resources about your first steps with Meteor out there, but after you’ve finished your first tutorials and want to build “something real” you should really focus and adapt the best practices for Meteor security.
Meteor makes it very easy and fast to get an app out of the door, but this awesomeness comes with the responsibility to check that you’ve closed all holes before publishing your app to your audience.

Note: This is just a primer to Meteor security. It doesn’t mean that everything is covered here. I’ve added a “Additional resources”-Section below where you can read on about this topic.

Autopublish and Insecure

Meteor makes it very easy to start and tinker around. But this “up and running in seconds” approach comes with a downside.
Let’s look into the packages-File which will be created if we start a brand new Meteor project:

cd my_new_meteor_project<br />
cat .meteor/packages<br />

We’ll see that three different packages are installed by default:

  • meteor-platform
  • autopublish
  • insecure

The meteor-platform-Package makes sure that the minimum functionality to run and play with Meteor is available at your fingertips.

But then there are also the autopublish- and insecure-Packages.

Autopublish ensures that all your database-Data (each and every document) will always be published automatically to all your clients.

Insecure makes it possible to write MongoDB-Commands (like e.g. Posts.insert({ title: ‘test’ })) which will run on the client (and can also be entered into the development-Console) and are than propagated to the server without any permission checks.

Those two packages are intended to make development easier.
But you should definitely remove those two packages if you want to publish your Meteor application to the public.

Just run this command to remove those two packages from your project:

meteor remove autopublish insecure<br />

Additionally I would recommend to remove the packages at the beginning of every new Meteor application (unless you really intend to use the provided functionality later on) so you don’t have any additional code-Changes once they have been removed.

Overpublishing data

Let’s take a look into a typical publication-Code:

Meteor.publish('posts', function() {<br />
return Posts.find();<br />
});<br />

Here we publish all our posts to the client.
What is bad about this? The bad thing is that you publish the whole document. This has a negative impact on the loading-Behavior of your application but also may leak document related information which shouldn’t be published (like e.g. a reports-Count which holds the value how often this post was reported by other users).

You should always only publish the necessary data to the clients. You could do this with the fields-Operator:

Meteor.publish('posts', function() {<br />
return Posts.find({}, {<br />
fields: {<br />
title: true,<br />
body: true<br />
}<br />
});<br />
});<br />

This way only the title and the body is published (and the _id which will be published by default). Everything else stays in the servers database. The reduced data-Size has also a positive effect on your applications load-Time.

Methods vs. Client-Side data manipulation

There are basically two different ways to manipulate database-Data in Meteor.
The first one is the manipulation of the data on the client with direct MongoDB-Calls to the Minimongo-Database which runs right inside the users browser. This changes are then reflected to the database on the server-Side.

The other possibility is the usage of Meteor-Methods. You define the method (which can run in both environments) and call the method from the client-Side.

When you use the way of client-Side data-Manipulation you write MongoDB commands which are directly executed. After you’ve removed the insecure-Package you have to write allow- and deny-Rules so that only permitted users can modify the data.
This is where the trouble starts.
It is very hard to write correct allow- and deny-Rules.

The Meteor community recently started a challenge where users should formulate allow- and deny-Rules for an update-Operation.
You should try to solve this challenge on your own. The results of this challenge outline a very important finding here.

It is damn hard to get it right.

Just stay away from client side data manipulation unless you can be sure that your allow- and deny rules are bullet proof.

If you really want easier control of your data manipulation than you should use a Meteor-Method and call that Method from the client. This way your security checks are less obfuscated and easier to implement.

The check-Package

One underestimated package which you should absolutely use is the check-Page which is supported by the Meteor development group.

Remember those days when you dealt with SQL-Injections?
There is something similar with databases such as MongoDB. And your Meteor application is vulnerable by default.

A common saying you should follow is “Never trust user inputs”. The check-Package makes it easy to check user-Input before it’s processed and ensures that the input the user has entered matches the expected type (e.g. we expect a String or an Integer). Meteor will throw an exception if the input-Data doesn’t match the pattern.

To use the check-Package simply add it to your Meteor project with the following command:

meteor add check<br />

After that you should can simply check the parameters like the following code-Example demonstrates:

// Using check in a publication<br />
Meteor.publish("post", function (_id) {<br />
check(_id, String);<br />
return Posts.find({_id: _id});<br />
});</p>
<p>// Using check in a method<br />
Meteor.methods({addPost: function (_id, title, body) {<br />
check(_id, String);<br />
check(title, String);<br />
check(body, String);<br />
// ...<br />
}});<br />

Always check the points of entry for user generated data with the help of the check-Package!

The audit-argument-checks-Package

So we’ve talked about the check-Package recently. But how do you know if you’ve checked all the parameters everywhere?

This is where the audit-argument-checks-Package comes in. It will ensure that you check all your parameters correctly. If you’ve missed a parameter-Check in e.g. a method, an exception is thrown and Meteor forbids this Method to proceed until you’ve added a corresponding parameter-Check.

This all works automatically after you’ve simply added the package with the following command:

meteor add audit-argument-checks<br />

The browser-policy-Package

This is yet another great package which makes your application more secure.
It implements the proposal of the browser policy approach.

The package provides you a toolset to control how the users browser behaves when e.g. external resources should be loaded or the application can be iFramed into another website.

Run the following command to add the package:

meteor add browser-policy<br />

After that you have different functions available.

Here’s an example which defines which external resources are allowed to be loaded into your Meteor application:

// This file lives on the server-Side<br />
BrowserPolicy.content.allowOriginForAll('http://ajax.googleapis.com');<br />
BrowserPolicy.content.allowOriginForAll('https://ajax.googleapis.com');<br />
BrowserPolicy.content.allowOriginForAll('http://*.youtube.com');<br />
BrowserPolicy.content.allowOriginForAll('https://*.youtube.com');<br />
BrowserPolicy.content.allowOriginForAll('http://*.amazonaws.com');<br />
BrowserPolicy.content.allowOriginForAll('https://*.amazonaws.com');<br />

Only Google, YouTube and Amazon AWS Scripts are allowed to be loaded. Everything else is blocked.

Follow the documentation of the package to see what else this package provides.

The profile-Field of Meteor.user()

Did you know that the profile-Field of Meteor.user() which is automatically provided by the Meteor accounts-Package is editable by the client and automatically published?

This field is a good place to store user-Related information such as the gender or the first- and last name. But nothing more. Don’t store sensitive information here.

It was recently proposed to deprecate the field because it’s behavior is often underestimated and misunderstood.

Using Meteor.userId() correctly

Let’s say you want to publish something that only the currently signed in user should see.
We pretend that we have a blogging platform where different companies and their users post about their companies strategy.
The currently signed in user has written a draft for a post which is not yet published because it contains sensitive information about the company’s earning call.

If we want to show the currently signed in user all his posts (including his drafts) we could do something like this:

Meteor.publish('posts', function(userId) {<br />
check(userId, String);<br />
return Posts.find({<br />
userId: userId<br />
});<br />
});<br />

“What is so bad about this?” you might think.

This publication can easily be used to expose other user posts.
Just open up the developer-Console from your browser and subscribe to the publication with another user id.

A better way to deal with such situation is to rely on the userId-Helper which is provided by the Meteor accounts package:

Meteor.publish('posts', function() {<br />
return Posts.find({<br />
userId: this.userId<br />
});<br />
});<br />

This way the userId of the currently signed in user is automatically used and you’re on the save side.

Conclusion / Additional resources

I hope that this blog post was helpful for you.
Security is a really important topic when developing applications.
This blog post is just a starting point. There are other great blog posts available which you should really check out:

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Philipp Muens
Philipp Muens

Written by Philipp Muens

👨‍💻 Maker — 👨‍🏫 Lifelong learner — Co-creator of the Serverless Framework — https://philippmuens.com

Responses (2)

Write a response