20 Apr

Security Tip for Developers: You Don’t Need to Restrict Direct Access to .php Files Twice

One of the items we check for during our security reviews of plugins selected by our customers is to see if the plugin’s .php files can be accessed directly when they are not intended to. While being able to access them directly when that isn’t necessary usually doesn’t have any security impact, it is easy to prevent that from happening and it could in some cases prevent serious issues, like a remote code execution (RCE) vulnerability we found being exploited in a security plugin last year.

Something we ran across recently in our monitoring of security changes made to plugins, which allows us to include data on vulnerabilities that you won’t find in other sources of WordPress plugin vulnerability data, indicates that developers implementing such protection don’t always understand what it is that they are implementing.

The changelog entry for the new version of a plugin was “Security plugin fix.”, but in looking at the changes made we found the only change was that the following code was added:

if ( ! defined( 'ABSPATH' ) ) { 
    exit; 
}

That is code that will prevent direct access to the file, as we will explain in a second.

That wasn’t a security fix since the code was added directly below this piece of code:

if ( ! defined( 'WPINC' ) )  die;

While that looks a bit different than the first one, it does exactly the same thing.

Then yesterday in the same plugin another change was made that added the following lines to the beginning of the code in a number of files:

if ( ! defined( 'WPINC' ) )  die; 
if ( ! defined( 'ABSPATH' ) ){ exit;  }

Seeing as both lines do the same thing, we were confused as to what might be going on. We wondered if we were missing something or if we could find an explanation as to what might be going on.

In our search for answers we couldn’t find anyone suggesting doing both of those, but we did run into a StackExchange question from someone wondering which to use. Seeing as there still seems to confusion on this let’s take a closer look at what they both do.

First let’s deal with the easy part of this, “die” and “exit” are the same. PHP just allows you terminate a script with either one.

The first part checks if a constant is defined, which for either “WPINC” or “ABSPATH” will not be true if you make a request directly to a .php file from a plugin. So which one is checked doesn’t make any difference for what is being done. When they are not defined “die” or “exit” executes and no more code in the file will run.

To full explain things though, “WPINC” or “ABSPATH” are constants that are defined when WordPress is loading. Normally, when requesting a front-end page  “ABSPATH” will be defined by the file /wp-load.php and “WPINC” will defined later when the file /wp-settings.php is loaded. Since neither one of those files will have been loaded when first accessing a plugin’s file directly, it doesn’t matter which one you check for, so you just need to add one of those line before the rest of the code in the file to prevent direct access.

16 May

Security Tip for Developers: Make Sure To Check a User’s Capabilities When Processing an Admin AJAX Request

One common cause of security issues with WordPress plugins that we continue to see happening is a failure to properly check on whether a user should be able to use admin AJAX functions they are sending requests to. Since the wp_ajax_ hook makes the AJAX function accessible to any logged in user, without checking their capabilities even a Subscriber level users can access functions meant only for Administrators.

In most cases you will also want to make sure you are protecting against cross-site request forgery (CSRF) in those ajax requests as well.

13 May

Security Tip for Developers: The is_admin() Function Doesn’t Tell You If Someone is an Administrator

One reoccurring cause of security issues in WordPress plugins is the misuse of the function is_admin(). Based on its name you might reasonably assume that it checks if someone is Administrator level user in WordPress and that seems to have tripped up lots of plugin developers. In reality it just “checks if the Dashboard or the administration panel is attempting to be displayed”. It will also “return true when trying to make an ajax request (both front-end and back-end requests)”.

How to Actually Check if Someone is an Administrator

If you need to check is someone is an Administrator you have several options.

One option is to use the function is_super_admin(), which will:

Determine if user is a network (super) admin. Will also check if user is admin if network mode is disabled.

You can also use the function current_user_can(), which can used to check the role of the user:

current_user_can('administrator')

or you can check if user has a capability, usually a check for the manage_options capability is used:

current_user_can('manage_options')

Checking a capability has the advantage that it will still work even if someone is using a non-standard roles in their WordPress installation.