Pete Corey Writing Work Contact

Anatomy of an Assessment

Written by Pete Corey on May 30, 2016.

Unfortunately I’m no longer offering Meteor security assessments as a professional service. If you’re interested in securing your Meteor application, please check out Secure Meteor. This post will remain published for historical reasons.

I’ve been quitely offering Meteor security assessments as a service for nearly a year now. In that time, I’ve worked with some amazing teams to analyze and assess the state of security in their Meteor applications.

An assessment is an in-depth, hands-on dive into your Meteor application. The goal of each assessment is to tease out any weak points that may leave your application susceptible to malicious users and outside attackers.

Why Security?

Security is fundamental to everything we do as software creators. It is an underlying assumption that makes everything we do possible. We spend countless hours building an effective team, developing amazing software and nurturing trust with our users, but all of that falls to the floor without security.

Imagine your company is doing well. Your application is a pleasure to use, and your user base is rapidly growing. You’ve attracted investors and you’ve built yourself an amazing team.

But suddenly, everything changes. A malicious user has managed to find and exploit a severe vulnerability within your application. Their attack has negatively impacted hundreds of users.

The hard earned trust between those affected users and your company vanishes instantly. Other users, when they learn of the attack, quickly begin to lose trust as well. Now, one of the first results when people google your product is a scathing TechCrunch article outlining the gory details of the attack. Soon, investors lose interest. With their lack of support and a rapidly dwindling user base, you realize that you won’t be able to make payroll this month.

The question of “why security?” is answered simply: Because everything we do depends on it.

How Do Assessments Work?

Before an assessment begins, I like to have a high-level discussion about your business, your application, and your users. This conversation lends insight into what you need and expect from an assessment. I also like to end this discussion with a quick architectural overview and a walkthrough of your application. This sets me up to get moving quickly once the assessment begins.

During the assessment, I sweep from the back-end of your application toward the front. Each assessment starts with a thorough review of any collection schemas being used, keeping a careful eye out for any type or structural inconsistencies or weaknesses that might lead to issues.

The bulk of each assessment is spent reviewing data paths between the client and the server. Meteor methods, publications, collection validators, and server-side routes are the primary targets of inspection. Each of these areas are reviewed with the following in mind:

  • Trusted fields are always being used, where applicable (e.g., this.userId).
  • All user actions are correctly authenticated and properly authorized.
  • All user provided data is thoroughly validated and sanitized.
  • User provided data is only trusted when appropriate.
  • Data is not being inadvertently or unexpectedly leaked to the client.
  • The risk of “Denial of Service” attacks are mitigated through proper error handling, rate limiting, and unblocking, when appropriate.

Next, attention shifts to the front-end of the application. I review the application’s Content Security Policy, investigate potential avenues for front-end attacks, and look for leaking secrets and configuration values.

Lastly, I run the project’s Meteor and Node.js dependencies through several automated scanners (Package Scan, NSP, and Snyk) looking for known vulnerabilities. As the results of these scans are sometimes only applicable in specific circumstances, I review the results and determine if they pose a threat to your application.

What Can I Expect From an Assessment?

While the most apparent goal of an assessment is to find vulnerabilities within your Meteor application, my real motivation is to help you build confidence around the security process.

It’s my hope that you leave the assessment with a more thorough understanding of Meteor security and how it applies to your application. In all of my communications, I try to include as much, if not more, “why” than “what”, in an attempt to equip you with the knowledge required to keep your application secure once I leave.

The final deliverables of an assessment include an in-depth report discussing the scope of the assessment, the methodologies used, an overview of each finding, and my final thoughts and suggestions in regards to your immediate course of action. The overview of each finding includes the finding’s severity, a brief description outlining why the issue exists and examples of how it can be exploited, a summary of the finding’s impact, and steps for remediation.

I like to present this final report in either a screen-sharing session, or an in-person meeting so that we can discuss the results in-detail. It’s my goal that you leave this final meeting with a complete understanding of everything in the report, along with a clear path forward.

Take a look at an example assessment report to get a clearer picture of what to expect.

Interested in an Assessment?

If you value the security of your Meteor application, I’d love to hear from you. Enter your email address below and I’ll send you a short questionnaire about your application. From there, I’ll reach out and start a discussion.

Unfortunately I’m no longer offering Meteor security assessments as a professional service. If you’re interested in securing your Meteor application, please check out Secure Meteor. This post will remain published for historical reasons.

I’m looking forward to hearing from you!

AWS Lambda First Impressions

Written by Pete Corey on May 24, 2016.

Lately, I’ve been paying quite a bit of attention to AWS Lambda.

Lambda is an Amazon Web Service designed to run small pieces of code in response to external stimuli (an endpoint is hit, a document is inserted into a database, etc…). The beautiful thing about Lambda is that your code is designed to run once, and you’re only charged for the amount of time your code is running.

A Node.js Script

To make things a little more concrete, let’s talk about my first baby-steps into working with Lambda.

I have a script-based tool that automates Bitcoin lending on the Poloniex exchange. Pre-Lambda, I implemented this tool as a Node.js script that spun up a local server, and executed a job every 15 minutes to “do stuff” (💸 💸 💸).

I wanted to move this script off of my local machine (mostly so I could close my laptop at night), so I began investigating my hosting and pricing options. On the low end of things, I could spin up a small DigitalOcean droplet for five dollars per month. Not bad, but I knew I’d be unnecessarily paying for quite a bit of idle server time.

I even considered buying a Raspberry PI for around forty dollars. I figured the upfront-costs of buying the device would be payed for within a year. After that initial investment, the power requirements would be negligible.

Meets AWS Lambda

Finally, I found Lambda. I quickly and painlessly modified my Node script to run once, manually deployed it to Lambda, and added a schedule trigger to run my script once every fifteen minutes.

Fast forward past a couple hours of fiddling and my script was working!

After monitoring my script for several days, I noticed that it took between one to two seconds to execute, on average. I added an execution hard-stop duration of three seconds to my Lambda function. With that, I knew that I would be charged for, at most, three seconds of up-time every fifteen minutes.

Using that data and Lambda’s pricing sheet, I calculated that at three seconds per execution with an execution every fifteen minutes, the yearly cost for running my script was, at most, at just under twenty two cents zero dollars.

I was shocked. $0.22/year! Thanks to Lambda’s free tier, hosting my script was free! Comparing that to DigitalOcean’s $60/year, or a Raspberry PI’s upfront cost of $40+ dollars, I had a clear winner.

Looking Forward

My first introduction to AWS Lambda left me impressed. Further research has left me even more excited. The possibilities of an scalable on-demand, event-driven infrastructure seem very attractive.

While I’m not totally re-assessing my software development stack, I’m definitely making a little room for Lambda. I’m already thinking about how I could have used it in the past to build more elegantly engineered, and cheaper solutions.

The Missing Link In Meteor's Rate Limiter

Written by Pete Corey on May 16, 2016.

Meteor’s DDPRateLimiter was released into Meteor in version 1.2 with surprisingly little fanfare. I say this is surprising because DDPRateLimiter helps minimize one of the most prevalent risks found in nearly all Meteor applications: Denial of Service attacks.

By putting hard limits on the rate at which people can call your methods and subscribe to your publications, you prevent them from being able to overrun your server with these potentially expensive and time consuming requests.

Unfortunately, Meteor’s DDPRateLimiter in its current form only partially solves the problem of easily DOS-able applications.

Meteor’s Rate Limiter

In this forum post, Adam Brodzinski, points out that the "meteor.loginServiceConfiguration" publication within the core accounts-base package is not being rate limited by default. He argues that this exposes a serious vulnerability to all Meteor applications using this package who haven’t taken extra precautions.

Without an established rate limit on this publication, any malicious user can potentially exploit it by making repeated subscriptions. These subscriptions flood the DDP queue and prevent other requests from being processed.

The exploit allows you to turn any meteor app on and off like a light switch.

These types of method and publication-based Denial of Service attacks are fairly well documented, and they’re even discussed in the Guide. Be sure to take a look if this kind of attack is new to you.

A Chink In The Armor

The initial vagueness of Adam’s post intrigued me. I started digging deeper into how and when DDPRateLimiter is used by Meteor core. My sleuthing payed off!

I found a chink in the rate limiter’s armor.

The DDPRateLimiter is invoked on the server whenever a subscription is made, and whenever a method is called. These invocations are fairly simple. They increment either a "subscription", or "method" counter and use these counters to check if the current rate of subscription or method calls exceeds any established limits. If the subscription/method exceeds a limit, an exception is thrown.

However, there’s a third type of DDP interaction that can be abused by malicious users: the DDP connection process itself.

Meteor users SockJS to handle its WebSocket connections. You’ll find the actual code that handles these connections in the ddp-server package. The DDP server extends this connection hooking functionality and registers callbacks for handling DDP-specific WebSocket messages.

If you look closely at the "connection" event handler, you’ll notice that it makes no attempt to rate limit the number of connection requests.

In fact, the DDPRateLimiter doesn’t even have a "connection" type. This means that a single user can repeatedly spam a Meteor server with DDP/WebSocket connection requests, all of which will be happily accepted until the server runs out of resources and chokes.

If abused, this can bring down a Meteor server in seconds.

Protecting Your Application

Sikka, like DDPRateLimiter, is another Meteor package designed to enforce rate limiting. Unfortunately, Sikka also won’t help protect against this particular kind of attack.

Sikka works by hooking into the processMessage method found in Meteor’s livedata server. Unfortunately, the processMessage method is called after a WebSocket connection is established. From within this method, we have no way of preventing abusive connection requests.

As discussed, DDPRateLimiter in its current form won’t prevent this type of Denial of Service attack.

Thinking out loud, one potential solution may be to modify Meteor core and add a third rate limiting type: "connection". This new rate limit type could be incremented and validation within each "connection" event:

self.server.on('connection', function (socket) {
  if (Package['ddp-rate-limiter']) {
    var DDPRateLimiter = Package['ddp-rate-limiter'].DDPRateLimiter;
    var rateLimiterInput = {
      type: "connection",
      connection: socket

    var rateLimitResult = DDPRateLimiter._check(rateLimiterInput);
    if (!rateLimitResult.allowed) {
      return socket.end();

If this technique works, extending the DDPRateLimiter in this way would give Meteor developers the power and flexibility to establish connection rate limits that make sense for their own applications.

Maybe this kind of functionality could even be implemented as a Meteor package, if the "connection" event listeners could be correctly overridden.

The surefire and recommended way of preventing this kind of attack is moving your Meteor application behind a proxy or load balancer like NGINX or HAProxy. Implementing rate limiting using these tools is fairly simple, and very effective.

Rate limiting on the network level means that abusively excessive requests to the /websocket HTTP endpoint will fail, which stops the WebSocket handshake process dead in its tracks, killing the connection before it hits your Meteor server.

I highly recommend moving your Meteor applications behind some kind of proxy layer, rather than exposing them directly to the world.

Final Thoughts

Denial of Service attacks in the Meteor world can be a scary thing to think about. The use of WebScokets and queue-based processing of DDP messages means that when they hit, they hit hard.

Fortunately, with the proper precautions, naive Denial of Service attacks are totally avoidable! Be sure to always rate limit your methods and publications, and move your application behind a proxy that does the same.