How To Stop XML-RPC Attacks While Allowing Jetpack & VaultPress Access

Folks in the WordPress space have been following the XML-RPC Quadratic Blowup attack fairly closely these last few weeks, as the attack has the capability to cripple sites running an unpatched version of WordPress within minutes, if not seconds. WordPress 3.9.2 (and 3.8.4 and 3.7.4) fixed the root problem, the vulnerability to the XML-RPC Quadratic Blowup attack, but it still allows XML-RPC functionality to be enabled.
Unfortunately, the latest rounds of patches aren’t fully sufficient in denying persistent crackers/lamers the capability of taking down a site sheerly through repeated requests to xmlrpc.php. Unless a site has some sort of throttling enabled (via, say, fail2ban [WordPress plugin available here]), a single user can, through sheer persistence, overwhelm a site.
For many WordPress users, XML-RPC isn’t necessary. It offers a convenient way to enable remote editors, such as MarsEdit or Windows Live Writer, to publish posts and pages directly to WordPress. It also allows other blogs to issue pingbacks to posts and pages. Useful, but not critical. In these cases, XML-RPC can be disabled in its entirety.
However, for users of two plugins, it’s absolutely critical: VaultPress and Jetpack. These two plugins rely upon XML-RPC to communicate with Automattic‘s servers to perform backups, sync comments, track stats, and a host of other functionality. Sites using Jetpack or Vaultpress (or both) have to have XML-RPC enabled or the plugins simply stop working. As Jetpack and VaultPress see wider distribution, disabling XML-RPC entirely becomes less of an option.
I recently had a site that was getting hammered by regular XML-RPC attacks and the user needed to have both VP and JP running. The site itself is running on the now-standard stack of Nginx and PHP-FPM, so here’s what I did:
Basing my work off of this helpful article, I tracked down all of Automattic’s current public IP addresses. (The list in the article itself isn’t fully applicable for two reasons — the addresses aren’t in CIDR notation and the instructions are for Apache instead of Nginx). I translated all of the ranges to CIDR netblocks using a handy CIDR subnet calculator. Then, inside of the site’s Nginx config file, I added the following block:

location /xmlrpc.php {
    include blockips.conf;
}

Then, I created /etc/nginx/blockips.conf, the contents of which were Automattic’s netblocks:

# Automattic's netblocks
allow 216.151.209.64/26;
allow 66.135.48.128/25;
allow 69.174.248.128/25;
allow 76.74.255.0/25;
allow 216.151.210.0/25;
allow 76.74.248.128/25;
allow 76.74.254.0/25;
allow 207.198.112.0/23;
allow 207.198.101.0/25;
allow 198.181.116.0/22;
allow 192.0.64.0/18;
allow 66.155.8.0/22;
allow 66.155.38.0/24;
allow 72.233.119.192/26;
allow 209.15.21.0/24;
deny all;

Then I restarted Nginx.
That’s the solution in its entirety. In plain language, if a request for XML-RPC isn’t coming from one of the Automattic netblocks, Nginx will simply drop the traffic on the floor, no questions asked — the traffic will never even make it to PHP in order to get processed. The nice thing about this approach is that, should I ever need to set this solution up on another host or add another domain to the same host, I can simply make sure blockips.conf is present and then just point that new config file at it, and if Automattic adds/changes their netblocks, I can simply change one file and have it apply to all of my sites automatically. I can even make it into a Puppet-managed asset and then put it anywhere I could feasibly need it.

1 Comment

Add Yours

Leave a Reply