Preparing for the Crater Conference

Written by Pete Corey on Feb 8, 2016.

Are you as pumped about the Crater Remote Conference as I am? There’s an amazing lineup of speakers scheduled, and I can’t wait to hear from each of them.

I’m currently in the process of putting the finishing touches on my talk. I’m planning on speaking about NoSQL Injection in modern web applications, with a heavy focus on MongoDB. I’m planning on doing most of the talk as a hands-on demonstration where we’ll attack a Meteor eCommerce application.

If you don’t have tickets yet, go buy them now!

The threat of SQL injection in modern web applications has been left by the wayside with the rise of NoSQL databases. Unfortunately, a new, but fundamentally similar threat has taken its place: NoSQL injection. Let’s take an in-depth look at this type of attack and the steps we can take to protect ourselves from it. The king is dead, long live the king!

Sending Emails Through Hidden Methods

Written by Pete Corey on Feb 1, 2016.

I was recently asked to take a look at a friend’s Meteor application. I was particularly interested in the security of the system, so I started snooping.

One of the first things I do when sleuthing my way through a new application is to dig into the client-side Meteor.connection._methodHandlers object, looking for interesting methods to play with. In this application, there was nothing interesting to be found; just the standard insert, update, and remove methods generated for every MongoDB collection.

When no methods appear in the Meteor.connection._methodHandlers object, it means that the developer either didn’t write any Meteor methods, or they wrote their methods in a server-only location which prevents them from being shipped and made visible to the client.

Finding Hidden Methods

If you’ve read my post on auditing Meteor methods, you’ll remember that even though a method may be hidden from the client, the client is still able to invoke that method. This means that while we may not see any interesting methods defined in the Meteor.connection._methodHandlers object, we might be able to find interesting method calls being done by the client.

To find these method calls, I opened up the minified Javascript source of the application, and started searching through the code for /\.call("/. Very quickly, I started seeing calls to hidden Meteor methods.

Sending Emails

While scanning through these calls to hidden methods, one method call in particular caught my eye:

...
Meteor.call("sendEmail", this.getEmail(), "Welcome...");
...

It looked as if this method took an email address and a message as arguments, and sent that message to the provided email address. Without access to the source of the method, I didn’t know if the method was doing some kind of validation on the provided email address. The only way to find out was to test it out. In my browser console, I tried the following:

Meteor.call("sendEmail", "hello@east5th.co", "Hi Pete!");

Sure enough, a few seconds later I received an email from the application owner with a message of "Hi Pete!". Uh oh.

With a little devious thinking, it’s not hard to imagine how this functionality could easily be abused by a potential hacker. Imagine someone leveraging your server and your SMTP account to send hundreds or thousands of spam emails. Or even worse, imagine an attacker impersonating the application or application owner and convincing users to click malicious links. This is a bad thing.

Locking It Down

The fix for this issue is fairly straightforward. Because any Meteor method can be called by any Meteor client, we simply shouldn’t have a method that sends arbitrary emails to arbitrary email addresses. Instead, we should take a step back and look at what we’re trying to do.

For example, if we’re attempting to send a welcome email to a user after they sign up for our application. A better way to handle this situation may be to hook into the user creation process and send the email there.

Or maybe an admin user may want the ability to send emails to users through some kind of admin panel. This could be implemented through a method that looks very similar to the sendEmail method we saw earlier, but with a few key differences. First, we would verify that the current user has the expected permissions to send the email. We would also verify that we’re sending the email to a user of the system, not an arbitrary email address:

sendEmail: function(userId) {
  var user = Meteor.users.findOne(userId);
  if (Roles.userIsInRole(this.userId, "admin") &&
      user && user.emails && user.emails[0].address) {
    Email.send(...);
  }
}

Final Thoughts

Isomorphism, or “universal code” is still a relatively new concept for many web developers. It can be difficult to cleanly divide server and client code in our minds when the distinction is anything but clear in the real world.

When writing Meteor applications, and Meteor methods in particular, it is incredibly important to always remember where your code can be run, and who can run it.

Scripting With MongoDB

Written by Pete Corey on Jan 25, 2016.

An often overlooked, but extremely powerful feature of MongoDB is the ability to execute Javascript directly within your database instance.

Recently, I was tasked with gathering some quick statistics on an ongoing A/B test. The system was splitting new users into two separate groups and presenting each group with slightly different experiences. The goal was to find the number of users per group, and find out the average number of “interactions” per user, per group.

I fired up Robomongo and went to work. The first thing I needed to do was define the two (or more) groups that I wanted to gather statistics on. To keep things simple for this example, let’s assume that users in Group A have a group field on their user document with a value of "A", and users in Group B have "B":

var groups = [
  {
    query: {
      group: "A", 
      createdAt: {$gte: ISODate("2016-01-01 05:00:00.000Z")}
    }
  },
  {
    query: {
      group: "B",
      createdAt: {$gte: ISODate("2016-01-01 05:00:00.000Z")}
    }
  }
];

Next, I wanted to loop through each of these groups, and find out how many users existed in each. This was fairly straight forward with some vanilla Javascript:

groups.forEach(function(group, index) {
  group.cursor = db.users.find(group.query);
  group.count = group.cursor.count();
  ...

In addition to calculating how many users were in each group, I also wanted to calculate the total number of interactions per group. In this simplified system, interactions are represented as a many-to-one mapping between the users collection and the interactions collection.

The first step to counting the number of interactions per group was to build a list of userIds per group. This is an easy task thanks to Mongo’s suite of database cursor methods:

  ...
  group.userIds = [];
  while (group.cursor.hasNext()) {
    group.userIds.push(group.cursor.next()._id);
  }
  ...

Now we can construct a query to find and count the total number of interactions per group:

  ...
  group.interactions = db.interactions.find({
    userId: {$in: group.userIds}
  }).count();
  ...

We now have all of the information we need. The last step is to calculate the number of interactions per user and print the results.

  ...
  print([
    "Group " + index + ":",
    "Total users: " + group.count,
    "Total interactions: " + group.interactions,
    "Interactions/user: " + group.interactions/group.count
  ].join("\n"));
});

The results of this script should give us something like this:

Group 0:
Total users: 1337
Total interactions: 84329
Interactions/user: 63.07329842931937

Group 1:
Total users: 1335
Total interactions: 79843
Interactions/user: 59.80749063670412

Not a bad result for a few minutes of coding. You can find the whole script here. Feel free to download it and modify it to fit your needs.

For more information on writing MongoDB scripts, take a look at this article.