Meteor Security Essentials
Security techniques that every Meteor developer should consider using in production.

by Ryan Glover (@themeteorchef)
http://talks.themeteorchef.com/meteor-security-essentials
http://github.com/themeteorchef/security-essentials
What we'll be covering
- Autopublish & Insecure
- Allow/Deny Rules
- Publications & Subscriptions
- Check & Audit Argument Checks
- Browser Policy
- Settings.json
- Additional Security Techniques
Autopublish & Insecure
- Both are installed by default after running meteor create <project-name>
- Autopublish: allows full read access on your database.
- Insecure: allows full write access (without allow/deny rules) on your database.
- Not intended for production.
- Best to remove them when you start a new project.
meteor remove autopublish meteor remove insecureAllow/Deny Rules
- Allow rules: set restrictions on client-side database operations.
- Deny rules: override Allow rules.
- Both are defined on a per collection basis.
- Four callbacks: insert, update, remove, and fetch (update and remove only).
- Four arguments: userId, document, fields (update only), and modifier (update only).
- Meteor checks Deny rules first.
- Admittedly a little confusing the first few times (see recommendation).
"When a client tries to write to a collection, the Meteor server first checks the collection's deny rules. If none of them return true then it checks the collection's allow rules. Meteor allows the write only if no deny rules return true and at least one allow rule returns true."
Allow/Deny Rules
Example: Defining Collection.allow() rules.
TodoItems = new Meteor.Collection('todo-items');
TodoItems.allow({
insert: function(userId, document){
// Allow insert if userId is equal to document.owner.
return userId == document.owner;
},
update: function(userId, document, fields, modifier){
// Allow update if userId is equal to document.owner and fields.badKey does NOT exist.
return ( userId == document.owner ) && !fields.badKey;
},
remove: function(userId, document){
// Allow remove if userId == document.owner.
return userId == document.owner;
},
fetch: ['owner']
});Allow/Deny Rules
Example: Defining Collection.deny() rules.
TodoItems = new Meteor.Collection('todo-items');
TodoItems.deny({
insert: function(userId, document){
// Deny insert if userId is NOT equal to document.owner.
return userId !== document.owner;
},
update: function(userId, document, fields, modifier){
// Deny update if userId is NOT equal to document.owner AND fields.badKey exists.
return ( userId !== document.owner ) && fields.badKey;
},
remove: function(userId, document){
// Deny remove if userId !== document.owner.
return userId !== document.owner;
}
});Allow/Deny Rules
Recommendation: force method-only database operations.
TodoItems.allow({
insert: function(userId, document){
return false;
},
update: function(userId, document, fields, modifier){
return false;
},
remove: function(userId, document){
return false;
}
});TodoItems.deny({
insert: function(userId, document){
return true;
},
update: function(userId, document, fields, modifier){
return true;
},
remove: function(userId, document){
return true;
}
});Adds a little extra work, but ensures that you don't forget to define rules and keeps your client-side controllers a little cleaner.
Allow/Deny Rules
Don't forget! Make sure to define Allow/Deny rules for the Meteor.users collection.
Meteor.users.allow({
insert: function(userId, document){
return false;
},
update: function(userId, document, fields, modifier){
return false;
},
remove: function(userId, document){
return false;
}
});Meteor.users.deny({
insert: function(userId, document){
return true;
},
update: function(userId, document, fields, modifier){
return true;
},
remove: function(userId, document){
return true;
}
});This one is easy to forget because we don't define the Meteor.users collection on our own. By default, it's helpful to prevent any client-side operations on Meteor.users.
Publications & Subscriptions
- Publications: defined on the server. Specify what data should be accessible on the client.
- Subscriptions: "connect" to publications on the client.
- Like a magazine subscription: I only get the magazines that I subscribe to.
- Be careful not to overpublish.
Meteor.publish('singleTodoList', function(todoListId) {
check(todoListId, String);
if (todoListId) {
var user = this.userId;
return TodoLists.find({"_id": todoListId, "owner": user}, {
fields: {
"name": 1
}
});
}
});Router.route('viewTodoList', {
path: "/lists/:_id",
template: "todoList",
subscriptions: function(){
return Meteor.subscribe('singleTodoList', this.params._id);
},
data: function(){
return TodoLists.findOne({"_id": this.params._id});
}
});Defining a publication on the server...
Subscribing on the client...
Check & Audit Argument Checks
- Two separate packages.
- Check: allows you to check the structure and type of arguments passed to methods.
- Audit: "watches" methods and throws an error if they're called without a Check().
- Help to confirm that only the data you expect is moving in/out of the database.
- Also apply to publications to prevent funky subscription arguments.
meteor add checkmeteor add audit-argument-checksCheck & Audit Argument Checks
Example: Using check() on a server-side method.
Meteor.methods({
insertTodoList: function(list){
check(list, {
"name": String,
"owner": String,
"extraKey1": String,
"extraKey2": Number
});
TodoLists.insert(list, function(error, id){
if (error) {
console.log(error);
} else {
// Do something here.
}
});
}
});
var list = {
"name": "Tacos to eat",
"owner": Meteor.userId(),
"missleLaunchCode": "xjo23jf023j2i3ofj2io3jf",
"extraKey1": String,
"extraKey2": Number
}
Meteor.call('insertTodoList', list, function(error, response){
if (error) {
console.log(error);
} else {
// Do something here.
}
});
Calling our method on the client...
This would fail based on our passed data...
Browser Policy
- Help to prevent cross-site scripting and clickjacking.
- Two parts: Content-Security-Policy and X-Frame-Options.
- CSP: Define where your application can load content from (own origin by default).
- XFO: Define which sites are allowed to "frame" your application (own origin by default).
- Both can be customized for your application.
// Customize to disallow any framing of your app (good if no iframes are being used).
BrowserPolicy.framing.disallow()
// Prevent any inline scripts from being used.
BrowserPolicy.content.disallowInlineScripts()
// Prevent any inline CSS styles from being used.
BrowserPolicy.content.disallowInlineStyles()
Settings.json
- Securely store sensitive configuration data outside of your logic.
- Options only exposed to server unless specified.
- Loaded on server startup using a flag or environment variable in production.
{
"public": {
"stripe": {
"publicKey": "123456abcdef"
}
},
"private": {
"stripe": {
"privateKey": "abcdef123456"
}
},
"alsoPrivate": "superSecretApiKey"
}Stored at /settings.json...
meteor --settings settings.jsonLoading on startup...
// Needs to load before the server starts.
process.env.METEOR_SETTINGS = "settings.json"
Loading via Environment Variable...
Settings.json
// "public" object available on both client/server
var stripePublic = Meteor.settings.public.stripe.apiKey;
// "private" object only available on server
var stripePublic = Meteor.settings.private.stripe.apiKey;
// "alsoPrivate" key only available on server
var alsoPrivate = Meteor.settings.alsoPrivate;Accessing values (client or server)
Additional Security Techniques
Adding schemas to your collections.
Using "Hooks" in Iron Router.
Creating server-only methods.
http://atmospherejs.com/aldeed/collection2
https://github.com/EventedMind/iron-router/blob/devel/Guide.md#hooks
https://github.com/themeteorchef/server-auth-token
Add client-side validation to forms.
https://github.com/themeteorchef/jquery-validation
Adding accounts.
http://docs.meteor.com/#/full/accounts_api
Resources
Discover Meteor: Meteor and Security
Josh Owens: Meteor Security 101
Emily Stark: Security Resources
https://www.discovermeteor.com/blog/meteor-and-security/
http://joshowens.me/meteor-security-101/
http://security-resources.meteor.com/
The Meteor Chef: Base
http://github.com/themeteorchef/base
The Meteor Chef: Roll Your Own Authentication
http://themeteorchef.com/recipes/roll-your-own-authentication
Thanks for listening!
If you'd like to learn more about Meteor, make sure to check out themeteorchef.com and follow @themeteorchef on Twitter.

Review these slides: http://talks.themeteorchef.com/meteor-security-essentials
Example code: http://github.com/themeteorchef/security-essentials
Meteor Security Essentials
By themeteorchef
Meteor Security Essentials
Security techniques that every Meteor developer should consider using in production.
- 4,398