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
meteor remove autopublish meteor remove insecure"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."
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']
});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;
}
});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.
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.
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...
meteor add checkmeteor add audit-argument-checksExample: 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...
// 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()
{
"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...
// "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)
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
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
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