An Easy Digital Download Software Licensing Server Attack

If you’ve purchased the Easy Digital Downloads Software Licensing add-on (min price $99/yr) and you have your eyes on API server traffic, you might have noticed that if your site runs CRON jobs as frequently as every minute, you are potentially getting hit by thousands of HTTP requests every hour (depending of course on how many license keys you’ve issued to customers). This can lead to DB errors and worse. That’s just not cool for the price.

EDD provides some “free” example integration code to get users started. Great, but we really shouldn’t use any volunteered code without an audit. Here’s the code they’ve provided:

/**
 * Initialize the updater. Hooked into `init` to work with the
 * wp_version_check cron job, which allows auto-updates.
 */
function edd_sl_sample_plugin_updater() {

	// To support auto-updates, this needs to run during the wp_version_check cron job for privileged users.
	$doing_cron = defined( 'DOING_CRON' ) && DOING_CRON;
	if ( ! current_user_can( 'manage_options' ) && ! $doing_cron ) {
		return;
	}

	// retrieve our license key from the DB
	$license_key = trim( get_option( 'edd_sample_license_key' ) );

	// setup the updater
	$edd_updater = new EDD_SL_Plugin_Updater(
		EDD_SAMPLE_STORE_URL,
		__FILE__,
		array(
			'version' => '1.0',                    // current version number
			'license' => $license_key,             // license key (used get_option above to retrieve from DB)
			'item_id' => EDD_SAMPLE_ITEM_ID,       // ID of the product
			'author'  => 'Easy Digital Downloads', // author of this plugin
			'beta'    => false,
		)
	);

}
add_action( 'init', 'edd_sl_sample_plugin_updater' );

Lines 7-11 of this PHP code indicate that as long as a site administrator is logged into the site and loads any page, or every time a CRON job runs, fire the EDD_SL_Plugin_Updater() class, which runs calls to the server running the API. Some CRON jobs run every minute, plus there are all the other cron jobs. Some admin users can load A LOT OF PAGES every minute, too.

YOIKS!

I added some checks to their suggested code so that even though the function runs with every page load (on the ‘init’ hook), it only ends up running if:

  • CRON is not running yet an administrator is viewing a plugin update screen
  • CRON is running and WP auto updates are turned on for the plugin

If auto updates are turned on this could still mean a lot of requests, but this certainly cuts way down on pointless API calls. Transients could also certainly be set; some plugins rate limit that way.

Slightly Optimized Version:

/**
 * Initialize the updater. Hooked into `init` to work with the
 * wp_version_check cron job, which allows auto-updates.
 */
function edd_sl_sample_plugin_updater() {

    if ( wp_installing() ) {
        return;
    }

    $doing_cron = defined( 'DOING_CRON' ) && DOING_CRON;

    if ( $doing_cron ) {
        /** 
         * If running cron, only continue if plugin auto updates are on
         * (The 'wp_version_check' CRON job supports auto-updates)
         */ 
        if ( 'yes' !== get_option( 'my_plugin_auto_updates', 'yes' ) ) { // if your plugin doesn't have a setting for this you can probably fetch the setting using hooks
            return;
        } else {
            // Probably wise to get a transient right here to slow this down...
        }
    } else {
        // If not doing cron, only check for updates for admin users
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }
        global $pagenow;
        // Cut back on requests by only continuing if on plugin updates page
        if ( isset( $pagenow ) && 'plugins.php' !== $pagenow && 'update-core.php' !== $pagenow ) {
            return;
        }
    }
    // retrieve our license key from the DB
    $license_key = trim( get_option( 'edd_sample_license_key' ) );

    // setup the updater
    $edd_updater = new EDD_SL_Plugin_Updater(
        EDD_SAMPLE_STORE_URL,
        __FILE__,
        array(
            'version' => '1.0',                    // current version number
            'license' => $license_key,             // license key (used get_option above to retrieve from DB)
            'item_id' => EDD_SAMPLE_ITEM_ID,       // ID of the product
            'author'  => 'Easy Digital Downloads', // author of this plugin
            'beta'    => false,
        )
    );

}
add_action( 'init', 'edd_sl_sample_plugin_updater' );

Get in touch if you need help with this or you have better ideas!