Meteor - Security

meteor

browserpolicy
https://github.com/djedi23/meteor-sanitize-html-client
http://docs.meteor.com/api/check.html#check
http://docs.meteor.com/packages/audit-argument-checks.html
https://atmospherejs.com/alanning/roles
http://security-resources.meteor.com/

https://www.youtube.com/watch?v=79uMp-S23MA
https://www.youtube.com/watch?v=A8TXDB6AJ34
https://www.youtube.com/watch?v=6_3pomxyp8M
http://www.livestream.com/githubpub/video?clipId=pla_3bc04cfc-4b3e-4810-acdd-8cfe79a3018e

https://meteorhacks.com/xss-and-meteor.html - done reading
http://stackoverflow.com/questions/21807229/meteor-js-and-csrf-xss-attacks - done reading
https://meteorhacks.com/introducing-portable-meteor-user.html - done reading
http://www.east5th.co/blog/2015/09/07/hijacking-meteor-accounts-with-xss/ - done reading
http://www.east5th.co/blog/2015/04/03/black-box-meteor-triple-brace-xss/ - done reading
https://dweldon.silvrback.com/browser-policy - done reading
https://github.com/aleksandryackovlev/xss-meteor - done reading
https://www.quora.com/Is-meteor-js-secure - done reading
https://www.discovermeteor.com/blog/meteor-and-security/ - done reading
http://www.meteorpedia.com/read/Why_Meteor#security - done reading
https://forums.meteor.com/t/xss-and-unintuitive-content-security-policies/19703 - done reading
https://medium.com/meteor-js/meteor-security-tips-6fdc28560895#.n5i1jf8e0 - done reading
https://www.reddit.com/r/Meteor/comments/2vbg4y/how_worried_should_i_be_about_xss_when_accepting/ - done reading
http://pivotallabs.com/meteor-and-web-security/ - done reading
http://www.east5th.co/blog/2016/03/07/cross-site-scripting-through-jquery-components/ - done reading
http://www.east5th.co/blog/2016/03/14/stored-xss-and-unexpected-unsafe-eval/ - done reading
https://www.meteor.com/blog/2014/03/14/session-cookies - done reading
https://www.meteor.com/blog/2013/10/27/defense-in-depth-securing-meteor-apps-with-content-security-policy - done reading
https://www.meteor.com/blog/2014/04/30/meteor-081-important-oauth-update - done reading
https://www.meteor.com/blog/2014/06/23/meteor-082 - done reading
http://stackoverflow.com/questions/24723/best-regex-to-catch-xss-cross-site-scripting-attack-in-java/535022#535022 - done reading
http://www.owasp.org/index.php/Category:OWASP_AntiSamy_Project
http://www.owasp.org/index.php/OWASP_Java_Table_of_Contents
http://www.owasp.org/index.php/How_to_perform_HTML_entity_encoding_in_Java
http://www.owasp.org/index.php/How_to_add_validation_logic_to_HttpServletRequest
NoScript
https://github.com/PHPIDS/
https://github.com/PHPIDS/PHPIDS/blob/master/lib/IDS/default_filter.xml
http://stackoverflow.com/questions/26029383/can-i-get-robust-xss-protection-in-cf11-that-i-can-apply-to-an-entire-site-witho
https://gist.github.com/dmh2000/1961957
http://www.gentics.com/Content.Node/guides/feature_validation.html

https://github.com/aldeed/meteor-simple-schema/issues/406 - done reading, good starting point
https://groups.google.com/forum/#!topic/meteor-talk/MHtV7xgnkIE - done reading, good
http://stackoverflow.com/questions/36240098/sanatize-html-on-server-in-schema - done reading, good
https://forums.meteor.com/t/example-of-sanitizing-string-with-autoform-simpleschema-insert-method/20125 - done reading, not much here
https://www.bountysource.com/teams/meteor-simple-schema/issues - done reading
http://stackoverflow.duapp.com/questions/36169232/how-to-sanitize-the-html-on-the-server-wysiwyg/38667534 - done reading, autoValue

https://themeteorchef.com/snippets/using-the-browser-policy-package/

http://security.stackexchange.com/questions/23734/what-type-of-attacks-can-be-used-vs-mongodb
http://css.csail.mit.edu/mylar/
https://github.com/strikeout/mylar

https://www.npmjs.com/package/sanitize-html
https://github.com/punkave/sanitize-html
https://www.npmjs.com/package/htmlstrip-native
https://www.npmjs.com/package/striptags
http://stackoverflow.com/questions/6659351/removing-all-script-tags-from-html-with-js-regular-expression - nothing much here

How can I automatically remove script tags from all form fields?

  1. Add a custom validation function that run on all fields in all schema Changing the value inside the validation does not seem to work
  2. Use the autoValue approach - http://stackoverflow.com/questions/36240098/sanatize-html-on-server-in-schema
  3. Add a cleaner function - https://www.bountysource.com/issues/24015066-add-custom-clean-functions
  4. Use the global collection hook approach https://github.com/matb33/meteor-collection-hooks approach
  5. Use the transform approach.
  6. Search for node package to remove script tag
  7. So far, from the Mongo.Collection documentation, the transform option seems to work only when getting the data out of the database. It does not seem to work when inserting or updating the database.

What are my strategies for minimizing the risks associated with XSS attacks?

  1. implement the browser-policy package
  2. add a global template helper to remove script tags from the content before it is displayed.
  3. continue to look for a Node package that sanitize all form submissions.

Why do we need to run 'meteor remove insecure'?

To demonstrate this problem, open the JavaScript console on the browser side and execute:

PlayersList.insert({
    name: "Fake Player",
    score: 1000,
    unwantedData: "Hello!"
});

The user could fill the database with any type (and any amount) of unwanted data, which is a gaping hole in our application’s security. Users also have to ability to modify and remove data from the database, meaning that, by default, they basically have complete, administrative permissions.

Every newly created Meteor project has the insecure package added by default. This is the package that allows us to edit the database from the client. It's useful when prototyping, but now we are taking off the training wheels. To remove this package, go to your app directory and run:

meteor remove insecure

If you try to use the app after removing this package, you will notice that none of the inputs or buttons work anymore. This is because all client-side database permissions have been revoked. Now we need to rewrite some parts of our app to use methods.

We need to define some methods. We need one method for each database operation we want to perform on the client. Methods should be defined in code that is executed on the client and the server

Meteor.methods({
  'tasks.insert'(text) {
    check(text, String);

    // Make sure the user is logged in before inserting a task
    if (! this.userId) {
      throw new Meteor.Error('not-authorized');
    }

    Tasks.insert({
      text,
      createdAt: new Date(),
      owner: this.userId,
      username: Meteor.users.findOne(this.userId).username,
    });
  },
  'tasks.remove'(taskId) {
    check(taskId, String);
    Tasks.remove(taskId);
  },
  'tasks.setChecked'(taskId, setChecked) {
    check(taskId, String);
    check(setChecked, Boolean);
    Tasks.update(taskId, { $set: { checked: setChecked } });
  },
});
Meteor.call('tasks.insert', text);

What happens when we run 'meteor remove insecure'?

After we removed the “insecure” package, we made it so users can only interact with the database by executing a Method.

Why do we have to run 'meteor remove autopublish'?

Until now, we have worked assuming the entire database is present on the client, meaning if we call Tasks.find() we will get every task in the collection. That's not good if users of our application want to store privacy-sensitive data. We need a way of controlling which data Meteor sends to the client-side database.

Just like with insecure in the last step, all new Meteor apps start with the autopublish package. We need to remove it:

meteor remove autopublish

Without the autopublish package, we will have to specify explicitly what the server sends to the client. The functions in Meteor that do this are Meteor.publish and Meteor.subscribe.

if (Meteor.isServer) {
  // This code only runs on the server
  Meteor.publish('tasks', function tasksPublication() {
    return Tasks.find({
      $or: [
        { private: { $ne: true } },
        { owner: this.userId },
      ],
    });
  });
}

And then let's subscribe to that publication when the body template is created:

Meteor.subscribe('tasks');

Calling Meteor.publish on the server registers a publication named "tasks". When Meteor.subscribe is called on the client with the publication name, the client subscribes to all the data from that publication, which in this case is all of the tasks in the database.

Why should we avoid using allow and deny?

In this guide, we’re going to take a strong position that using allow or deny to run MongoDB queries directly from the client is not a good idea. The main reason is that it is hard to follow the principles outlined above. It’s extremely difficult to validate the complete space of possible MongoDB operators, which could potentially grow over time with new versions of MongoDB.

There have been several articles about the potential pitfalls of accepting MongoDB update operators from the client, in particular the Allow & Deny Security Challenge and its results, both on the Discover Meteor blog.

Given the points above, we recommend that all Meteor apps should use Methods to accept data input from the client, and restrict the arguments accepted by each Method as tightly as possible.

Here’s a code snippet to add to your server code which disables client-side updates on a collection. This will make sure no other part of your app can use allow:

// Deny all client-side updates on the Lists collection
Lists.deny({
  insert() { return true; },
  update() { return true; },
  remove() { return true; },
});

Why should we do proper validation?

Methods are the way your Meteor server accepts inputs and data from the outside world, so it’s natural that they are the most important topic for security. If you don’t properly secure your Methods, users can end up modifying your database in unexpected ways - editing other people’s documents, deleting data, or messing up your database schema causing the app to crash.

Consider that if you are writing unit tests for your Methods, you would need to test all possible kinds of input to the Method; validating the arguments restricts the space of inputs you need to unit test, reducing the amount of code you need to write overall. It also has the extra bonus of being self-documenting; someone else can come along and read the code to find out what kinds of parameters a Method is looking for.

Just as an example, here’s a situation where not checking arguments can be disastrous:

Meteor.methods({
  removeWidget(id) {
    if (! this.userId) {
      throw new Meteor.Error('removeWidget.unauthorized');
    }

    Widgets.remove(id);
  }
});

If someone comes along and passes a non-ID selector like {}, they will end up deleting the entire collection. To help you write good Methods that exhaustively validate their arguments, we’ve written a simple wrapper package for Methods that enforces argument validation.

When should we pass the userId as an argument?

The this context inside every Meteor Method has some useful information about the current connection, and the most useful is this.userId. This property is managed by the DDP login system, and is guaranteed by the framework itself to be secure following widely-used best practices.

Given that the user ID of the current user is available through this context, you should never pass the ID of the current user as an argument to a Method. This would allow any client of your app to pass any user ID they want. Let’s look at an example:

// #1: Bad! The client could pass any user ID and set someone else's name
setName({ userId, newName }) {
  Meteor.users.update(userId, {
    $set: { name: newName }
  });
}

// #2: Good, the client can only set the name on the currently logged in user
setName({ newName }) {
  Meteor.users.update(this.userId, {
    $set: { name: newName }
  });
}

The only times you should be passing any user ID as an argument are the following:

  1. This is a Method only accessible by admin users, who are allowed to edit other users. See the section about user roles to learn how to check that a user is in a certain role.
  2. This Method doesn’t modify the other user, but uses it as a target; for example, it could be a Method for sending a private message, or adding a user as a friend.

Why should we implement one Method per action?

The best way to make your app secure is to understand all of the possible inputs that could come from an untrusted source, and make sure that they are all handled correctly. The easiest way to understand what inputs can come from the client is to restrict them to as small of a space as possible. This means your Methods should all be specific actions, and shouldn’t take a multitude of options that change the behavior in significant ways. The end goal is that you can easily look at each Method in your app and validate or test that it is secure. Here’s a secure example Method from the Todos example app:

export const makePrivate = new ValidatedMethod({
  name: 'lists.makePrivate',
  validate: new SimpleSchema({
    listId: { type: String }
  }).validator(),
  run({ listId }) {
    if (!this.userId) {
      throw new Meteor.Error('lists.makePrivate.notLoggedIn',
        'Must be logged in to make private lists.');
    }

    const list = Lists.findOne(listId);

    if (list.isLastPublicList()) {
      throw new Meteor.Error('lists.makePrivate.lastPublicList',
        'Cannot make the last public list private.');
    }

    Lists.update(listId, {
      $set: { userId: this.userId }
    });

    Lists.userIdDenormalizer.set(listId, this.userId);
  }
});

You can see that this Method does a very specific thing - it just makes a single list private. An alternative would have been to have a Method called setPrivacy, which could set the list to private or public, but it turns out that in this particular app the security considerations for the two related operations - makePrivate and makePublic - are very different. By splitting our operations into different Methods, we make each one much clearer. It’s obvious from the above Method definition which arguments we accept, what security checks we perform, and what operations we do on the database.

However, this doesn’t mean you can’t have any flexibility in your Methods. Let’s look at an example:

const Meteor.users.methods.setUserData = new ValidatedMethod({
  name: 'Meteor.users.methods.setUserData',
  validate: new SimpleSchema({
    fullName: { type: String, optional: true },
    dateOfBirth: { type: Date, optional: true },
  }).validator(),
  run(fieldsToSet) {
    Meteor.users.update(this.userId, {
      $set: fieldsToSet
    });
  }
});

The above Method is great because you can have the flexibility of having some optional fields and only passing the ones you want to change. In particular, what makes it possible for this Method is that the security considerations of setting one’s full name and date of birth are the same - we don’t have to do different security checks for different fields being set. Note that it’s very important that the $set query on MongoDB is generated on the server - we should never take MongoDB operators as-is from the client, since they are hard to validate and could result in unexpected side effects.

Why should we refactor to use security roles?

You might run into a situation where many Methods in your app have the same security checks. This can be simplified by factoring out the security into a separate module, wrapping the Method body, or extending the Mongo.Collection class to do security inside the insert, update, and remove implementations on the server. However, implementing your client-server communication via specific Methods is still a good idea rather than sending arbitrary update operators from the client, since a malicious client can’t send an update operator that you didn’t test for.

Why should we do rate limiting?

Just like REST endpoints, Meteor Methods can easily be called from anywhere - a malicious program, script in the browser console, etc. It is easy to fire many Method calls in a very short amount of time. This means it can be easy for an attacker to test lots of different inputs to find one that works. Meteor has built-in rate limiting for password login to stop password brute-forcing, but it’s up to you to define rate limits for your other Methods.

In the Todos example app, we use the following code to set a basic rate limit on all Methods:

// Get list of all method names on Lists
const LISTS_METHODS = _.pluck([
  insert,
  makePublic,
  makePrivate,
  updateName,
  remove,
], 'name');

// Only allow 5 list operations per connection per second
DDPRateLimiter.addRule({
  name(name) {
    return _.contains(LISTS_METHODS, name);
  },

  // Rate limit per connection ID
  connectionId() { return true; }
}, 5, 1000);

This will make every Method only callable 5 times per second per connection. This is a rate limit that shouldn’t be noticeable by the user at all, but will prevent a malicious script from totally flooding the server with requests. You will need to tune the limit parameters to match your app’s needs.

How can we restrict fields?

Mongo.Collection#find has an option called fields which lets you filter the fields on the fetched documents. You should always use this in publications to make sure you don’t accidentally publish secret fields.

For example, you could write a publication, then later add a secret field to the published collection. Now, the publication would be sending that secret to the client. If you filter the fields on every publication when you first write it, then adding another field won’t automatically publish it.

// #1: Bad! If we add a secret field to Lists later, the client
// will see it
Meteor.publish('lists.public', function () {
  return Lists.find({userId: {$exists: false}});
});

// #2: Good, if we add a secret field to Lists later, the client
// will only publish it if we add it to the list of fields
Meteor.publish('lists.public', function () {
  return Lists.find({userId: {$exists: false}}, {
    fields: {
      name: 1,
      incompleteCount: 1,
      userId: 1
    }
  });
});

If you find yourself repeating the fields often, it makes sense to factor out a dictionary of public fields that you can always filter by, like so:

// In the file where Lists is defined
Lists.publicFields = {
  name: 1,
  incompleteCount: 1,
  userId: 1
};

Now your code becomes a bit simpler:

Meteor.publish('lists.public', function () {
  return Lists.find({userId: {$exists: false}}, {
    fields: Lists.publicFields
  });
});

Is a publication reactive?

No. Publications are not reactive, and they only re-run when the currently logged in userId changes, which can be accessed through this.userId. Because of this, it’s easy to accidentally write a publication that is secure when it first runs, but doesn’t respond to changes in the app environment. Let’s look at an example:

// #1: Bad! If the owner of the list changes, the old owner will still see it
Meteor.publish('list', function (listId) {
  check(listId, String);

  const list = Lists.findOne(listId);

  if (list.userId !== this.userId) {
    throw new Meteor.Error('list.unauthorized',
      'This list doesn\'t belong to you.');
  }

  return Lists.find(listId, {
    fields: {
      name: 1,
      incompleteCount: 1,
      userId: 1
    }
  });
});

// #2: Good! When the owner of the list changes, the old owner won't see it anymore
Meteor.publish('list', function (listId) {
  check(listId, String);

  return Lists.find({
    _id: listId,
    userId: this.userId
  }, {
    fields: {
      name: 1,
      incompleteCount: 1,
      userId: 1
    }
  });
});

In the first example, if the userId property on the selected list changes, the query in the publication will still return the data, since the security check in the beginning will not re-run. In the second example, we have fixed this by putting the security check in the returned query itself.

Unfortunately, not all publications are as simple to secure as the example above. For more tips on how to use reywood:publish-composite to handle reactive changes in publications, see the data loading article.

How can we pass options?

For certain applications, for example pagination, you’ll want to pass options into the publication to control things like how many documents should be sent to the client. There are some extra considerations to keep in mind for this particular case.

  1. Passing a limit: In the case where you are passing the limit option of the query from the client, make sure to set a maximum limit. Otherwise, a malicious client could request too many documents at once, which could raise performance issues.
  2. Passing in a filter: If you want to pass fields to filter on because you don’t want all of the data, for example in the case of a search query, make sure to use MongoDB $and to intersect the filter coming from the client with the documents that client should be allowed to see. Also, you should whitelist the keys that the client can use to filter - if the client can filter on secret data, it can run a search to find out what that data is.
  3. Passing in fields: If you want the client to be able to decide which fields of the collection should be fetched, make sure to intersect that with the fields that client is allowed to see, so that you don’t accidentally send secret data to the client.

How can we implement secret server code?

While the client-side code of your application is necessarily accessible by the browser, every application will have some secret code on the server that you don’t want to share with the world. Secret business logic in your app should be located in code that is only loaded on the server. This means it is in a server/ directory of your app, in a package that is only included on the server, or in a file inside a package that was loaded only on the server.

If you have a Meteor Method in your app that has secret business logic, you might want to split the Method into two functions - the optimistic UI part that will run on the client, and the secret part that runs on the server. Most of the time, putting the entire Method on the server doesn’t result in the best user experience. Let’s look at an example, where you have a secret algorithm for calculating someone’s MMR (ranking) in a game:

// In a server-only file
MMR = {
  updateWithSecretAlgorithm(userId) {
    // your secret code here
  }
}
// In a file loaded on client and server
const Meteor.users.methods.updateMMR = new ValidatedMethod({
  name: 'Meteor.users.methods.updateMMR',
  validate: null,
  run() {
    if (this.isSimulation) {
      // Simulation code for the client (optional)
    } else {
      MMR.updateWithSecretAlgorithm(this.userId);
    }
  }
});

Note that while the Method is defined on the client, the actual secret logic is only accessible from the server. Keep in mind that code inside if (Meteor.isServer) blocks is still sent to the client, it is just not executed. So don’t put any secret code in there.

Secret API keys should never be stored in your source code at all.

How can we access settings?

You can pass settings to your app through a settings file or an environment variable. Most of your app settings should be in JSON files that you pass in when starting your app. You can start your app with a settings file by passing the —settings flag:

# Pass development settings when running your app locally
meteor --settings development.json

# Pass production settings when deploying your app to Galaxy
meteor deploy myapp.com --settings production.json

Here’s what a settings file with some API keys might look like:

{
  "facebook": {
    "clientId": "12345",
    "secret": "1234567"
  }
}

In your app’s JavaScript code, these settings can be accessed from the variable Meteor.settings.

In most normal situations, API keys from your settings file will only be used by the server, and by default the data passed in through —settings is only available on the server. However, if you put data under a special key called public, it will be available on the client. You might want to do this if, for example, you need to make an API call from the client and are OK with users knowing that key. Public settings will be available on the client under Meteor.settings.public.

What additional things do we need to do for the accounts-facebook package?

For the accounts-facebook package to pick up these keys, you need to add them to the service configuration collection in the database. Here’s how you do that:

First, add the service-configuration package:

meteor add service-configuration

Then, upsert into the ServiceConfiguration collection:

ServiceConfiguration.configurations.upsert({
  service: "facebook"
}, {
  $set: {
    clientId: Meteor.settings.facebook.clientId,
    loginStyle: "popup",
    secret: Meteor.settings.facebook.secret
  }
});

Now, accounts-facebook will be able to find that API key and Facebook login will work properly.

How can we force or use SSL?

You can ensure that any unsecured connection to your app redirects to a secure connection by adding the force-ssl package.

  1. On Galaxy, most things are set up for you, but you need to add a certificate. See the help article about SSL on Galaxy.
  2. If you are running on your own infrastructure, there are a few options for setting up SSL, mostly through configuring a proxy web server. See the articles: Josh Owens on SSL and Meteor, SSL on Meteorpedia, and Digital Ocean tutorial with an Nginx config.

What are the parameters for the publish method?

Meteor.publish('thePlayers', function(){
    var currentUserId = this.userId;
    return PlayersList.find({ createdBy: currentUserId });
});

The first parameter is simply a name. The second parameter is a function. It’s within this function that we specify what data should be available to users of the application. By publishing data, we’re essentially transmitting that data from the server and other into the ether. But for the data to become available to users from inside their browser, we must subscribe to that data from the client-side.

The only argument we need to pass into the Meteor.subscribe function is the name of a publish function:

Meteor.subscribe('thePlayers');

What should we check for?

This is a collection of points to check about your app that might catch common errors. However, it’s not an exhaustive list yet—if we missed something, please let us know or file a pull request!

  1. Make sure your app doesn’t have the insecure or autopublish packages.
  2. Validate all Method and publication arguments, and include the audit-argument-checks to check this automatically.
  3. Deny writes to the profile field on user documents.
  4. Use Methods instead of client-side insert/update/remove and allow/deny.
  5. Use specific selectors and filter fields in publications.
  6. Don’t use raw HTML inclusion in Blaze unless you really know what you are doing.
  7. Make sure secret API keys and passwords aren’t in your source code.
  8. Secure the data, not the UI - redirecting away from a client-side route does nothing for security, it’s just a nice UX feature.
  9. Don’t ever trust user IDs passed from the client. Use this.userId inside Methods and publications.
  10. Set up browser policy, but know that not all browsers support it so it just provides an extra layer of security to users with modern browsers.

Can meteor template guard against all types of XSS attack?

No. As an example, let’s say I’m working on a community forum site with Meteor that allows users to post links. One of the typical templates looks like this:

<a href='{{url}}'>{{title}}</a>

This seems perfectly fine. But, let’s say someone adds a post with URL javascript:alert('hacked'). Then the rendered DOM will be something like this:

<a href='javascript:alert("hacked")'>
    Google has direct connections with NSA. They were lying to us.
</a>

When you click that link, you’ll see the alert box as shown below. This is just one type of XSS attack. The JavaScript code above can be any malicious code.

How can XSS harm Meteor application?

  1. With XSS, malicious users have access to the logged in user’s DDP connection and can do whatever they need, including altering mongodb and the server state, where the logged in user allows it.
  2. Since Meteor uses localStorage for the session persistent, malicious user can steal a logged in user’s identity.

How does the browser-policy package help guard against XSS?

Meteor has a package called browser-policy, which helps you to create CSP rules very easily. Once you add the package, you will get the following CSP policies by default.

default-src 'self'; script-src 'self' 'unsafe-inline'; connect-src * 'self'; img-src data: 'self'; style-src 'self' 'unsafe-inline';

This is how we can interpret the above rules, in plain text:

  1. You will only be able to load resources from the current origin of your app
  2. You won’t be able to execute eval or similar functionalities
  3. You will be able to use inline scripts and so your app is vulnerable to potential XSS attacks, as I showed in the beginning of the article.
  4. You will be able connect to any external service via AJAX, WebSockets, and similar techniques, which also makes your app vulnerable to potential XSS attacks.

These restrictions add some level of protection, but the third and fourth points make your app still vulnerable to XSS.

Does the browser-policy guard against all XSS attack?

No. It depends on what policy we declare.

How can we implement the 'Block Everything, then Allow As Necessary' approach using the browser-policy package?

meteor add browser-policy

Below is a sample policy.js that you can put anywhere in your /server directory.

// Disallows:
BrowserPolicy.framing.disallow();
BrowserPolicy.content.disallowInlineScripts();
BrowserPolicy.content.disallowEval();
BrowserPolicy.content.disallowConnect();

// Allows:
BrowserPolicy.content.allowInlineStyles();
BrowserPolicy.content.allowFontDataUrl();

BrowserPolicy.content.allowScriptOrigin("*.google-analytics.com");
BrowserPolicy.content.allowImageOrigin("*.google-analytics.com");

// The disallowConnect statement will prevent us from using Meteor’s DDP connection, 
// so we should also add the following rules:
var rootUrl = __meteor_runtime_config__.ROOT_URL;
BrowserPolicy.content.allowConnectOrigin(rootUrl);
BrowserPolicy.content.allowConnectOrigin(rootUrl.replace('http', 'ws'));

These settings do the following:

  1. prevent the site from being embedded inside a frame / iframe (recommended) or framed
  2. prevent inline scripts (recommended)
  3. prevent eval (strongly recommended)
  4. allow inline styles (we found this necessary for Google Fonts to work)
  5. allow fonts to be loaded via data URLs (we load our icon font this way)
  6. trust external scripts only from Google Analytics, Mixpanel, and Zendesk
  7. external scripts must be loaded over HTTPS

If you are hosting on meteor.com, you need to add rules like the ones shown below. This is not an ideal solution, since your app is allowed to connect to any app hosted on meteor.com.

BrowserPolicy.content.allowConnectOrigin("https://*.meteor.com");
BrowserPolicy.content.allowConnectOrigin("wss://*.meteor.com");

Since we blocked all the origins, you will need to allow resources as shown below. Adding something like Google Analytics is tricky, since you need to expand its asynchronous code and allow it to run without inline scripts and eval. To do this, first add the following code inside the HTML head:

<script type="text/javascript" src="//www.google-analytics.com/analytics.js"></script>
<script type="text/javascript" src="/ga.js"></script>

Now create a file called ga.js into your public folder and add following content:

ga("create", "YOUR_GA_ID", "YOUR_WEBSITE");
ga("send", "pageview");

Finally, add the following CSP permissions:

//for the script
//for the tracking pixel

Use the Browser Policy API and CSP Docs to allow origins for all the external resources you load. I know this seems hard, but it’s well worth the trouble to prevent XSS.

let userInput = "<script>alert('hi!');</script>";
$(someElement).html(userInput);

Unintuitively, this type of XSS is not prevented by disallowing inline scripts (BrowserPolicy.content.disallowInlineScripts()) within your Meteor application. The injected Javascript is actually executed as part of an eval statement, and can only be prevented by your CSP if you're disallowing unsafe eval (BrowserPolicy.content.disallowEval()). We should all remember that while browser-policy is a useful tool, it's not a panacea. We should still actively try to find and fix XSS vulnerabilities within our applications!

Are Meteor application vulnerable to CSRF attacks?

No. CSRF attacks are not possible in Meteor as the framework itself doesn't use cookies at all and prefers HTML5 localStorage which is much harder to spoof. http://stackoverflow.com/questions/21807229/meteor-js-and-csrf-xss-attacks

How does the 'portable user' hack work?

I’m really not sure whether “portable” is the right term to use here. However, I’ll show you an easy way to transfer login states across browsers without re-entering username/password combinations. You are not required to use any additional tool or code modifications. You need a browser with a JavaScript Console (I prefer Google Chrome). Visit any Meteor web app, login as a legitimate user, and paste the following code to the console:

loginToken=localStorage.getItem("Meteor.loginToken");
var userId=localStorage.getItem("Meteor.userId");
var str='localStorage.setItem("Meteor.loginToken", "'+loginToken+'");';
str+='localStorage.setItem("Meteor.userId", "'+userId+'");';
alert(str);

You will then receive an alert with a code. Copy that code, it is the portable version of you. This code can be passed on to other users via chat, email, or even SMS. Once they receive the code, they can use it to login to the meteor app as you. Visit the related meteor app using Google Chrome, and execute the code you generated in the browser console. Wait a few seconds and … Voila! You are now successfully logged in as the user who created the “code”.

You can use a portable user to test your app between browsers. You can use it as a debug tool. You can allow your friends to use your account on your behalf (without sharing passwords). You can even steal someone’s identity very easily.

Meteor uses a token to remember the user’s login state. It is identical to the “Remember Me” Cookie in traditional web apps. But Meteor uses LocalStorage instead of Cookies. This hack simply picks “loginToken” and the “userId” from localStorage. Then it creates a valid JS code which sets the above into localStorage again (That’s the portable user). Once you paste the above JS code to the browser, Meteor detects the change and performs the login process against the token.

How can we allow images from a specific origin?

BrowserPolicy.content.allowImageOrigin("https://mycdn.com")

How can we disallow inline style?

If your app does not use any inline CSS, call BrowserPolicy.content.disallowInlineStyles() to reduce the ability of an attacker to control your app’s appearance.

How can we disallow inline script?

If your app does not use any inline scripts or you are willing to move those inline scripts to separate files, call BrowserPolicy.content.disallowInlineScripts(). Note that this will add an extra external script tag to your page for Meteor runtime configuration, which is usually inlined when the CSP allows inline scripts.

Why should we be as specific as possible when adding domains to our browser policy file?

We should be as specific as possible when adding domains. For example if you have a CloudFront distribution, you should add an entry like 'SNkmhiE2b2fQiCxB3.cloudfront.net', rather than '*.cloudfront.net'.

What are some potential issues associate with the browser-policy package?

  1. One of the 3rd-party scripts we required was calling eval. We had to temporarily relax that restriction until the company modified its code.
  2. Our typeface broke once when Google Fonts changed its domain.

When should we use double braces instead of triple braces?

Always use double braces, except for instances where you know for certain that the content of what you are sending to the browser came from a trusted source.

What does the double braces do?

It escape the angle brackets and therefore cause the content to be render as text instead of HTML code.

Is your Meteor application solely exposed to XSS attacks through the use of triple braces?

No. Your Meteor application is not solely exposed to cross-site scripting through the use of triple-brace tags. Malicious HTML/JavaScript can be introduced into your Blaze-powered application through the use of SafeString, dynamic attributes, and dynamic attribute values, to name a few. When using these techniques with user-provided data, be especially sure that you’re properly sanitizing or validating the data before sending it into the DOM. http://www.east5th.co/blog/2015/09/07/hijacking-meteor-accounts-with-xss/

How can we find all the places that use triple braces?

If we have access to the code, we can grep for triple braces. If we do not have access to the code, we can use the approach mentioned on http://www.east5th.co/blog/2015/04/03/black-box-meteor-triple-brace-xss/

How does the browser-policy package work?

Under the covers, browser-policy implements a combination of X-Frame-Options headers and content security policy directives which allow you to do things like ward off XSS attacks and prevent your app from being framed.

How can we get the browser-policy package to work with Nginx?

In order for the browser-policy package to work with Nginx, we had to increase the default proxy buffer sizes. The following settings worked for us, but YMMV:

# Increase the proxy buffers for meteor browser-policy.
proxy_buffer_size       128k;
proxy_buffers           4 256k;
proxy_busy_buffers_size 256k;

These changes should go in the http secion of your configuration file - typically located in /etc/nginx/nginx.conf.

What is the purpose of the aleksandryackovlev/xss-meteor package?

It contains template helpers to prevent xss. This package is a simple meteor.js wrapper for the xss-filter npm package. I am not sure how to use this package yet. Maybe I need to do a search to see how other people are using it or if there is any tutorial on how to use it.

What is the purpose of the Blaze._escape function?

It takes in any string and escapes any HTML special characters into their corresponding HTML encoded form.

Template.choices.onRendered(function() {
  let options = Choices.find().fetch().map(choice => {
    return {
      label: Blaze._escape(choice.name),
      value: choice._id_
    };
  });
  this.$("select").dropdown({
    options: options
  });
});

How can we log out all users?

db.users.update({}, { $set: { 'services.resume.loginTokens': [] } }, { multi: true });

What is the purpose of the 'meteor list-sites' command?

A new meteor list-sites command prints out the list of all the sites you've deployed using your Meteor developer account.

Why should we use AntiSamy?

Use AntiSamy. It works against XSS by actually parsing the HTML, and then traversing the DOM and removing anything that's not in the configurable whitelist. The major difference is the ability to gracefully handle malformed HTML. The best part is that it actually unit tests for all the XSS attacks on the above site. Besides, what could be easier than this API call:

public String toSafeHtml(String html) throws ScanException, PolicyException {

    Policy policy = Policy.getInstance(POLICY_FILE);
    AntiSamy antiSamy = new AntiSamy();
    CleanResults cleanResults = antiSamy.scan(html, policy);
    return cleanResults.getCleanHTML().trim();
}
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License