Five Ways to Secure Your WordPress Plugins

Plugins allow us to easily modify, customize, and enhance the already amazing WordPress platform. They also allow us a way to share enhancements with those who aren’t able to write their own code. With this freedom and flexibility, it is our responsibility as developers to maintain the same level of care and security in our plugins that everyone expects of the WordPress platform.

I’d like to run through some basic security practices you should include in your plugin where necessary. This information is not just important for developers to know but can be invaluable to non-developers wanting to review plugins before installing and activating them on their sites.

Prevent direct access to PHP files

When WordPress loads it looks for all currently active plugins and loads the appropriate file from each active plugin’s directory. Because these PHP files are most likely located within the web server’s document root (ex: /srv/www/yourgroovydomain.com/wp-content/plugins/my-plugin/my-plugin.php with yourgroovydomain.com being the root directory), anyone can access them from a web browser or other program by going to http://yourgroovydomain.com/wp-content/plugins/my-plugin/my-plugin.php. This is bad.

We must account for the possibility that attackers may exploit this to harm our user’s website or their hosting provider’s server. Preventing this is very easy and should be the absolute first thing you add to the top of your PHP files — before any other code is executed.

We want to prevent public access to this file but still allow WordPress to load the file internally. The most common solution is to check for the existance of the ABSPATH constant defined by WordPress when your site is loaded. If the constant exists, then WordPress is requesting access to load this file. If the constant does not exist, someone is trying to directly access this file and we want to stop them immediately.

<?php defined( 'ABSPATH' ) or die();

Conduct capability checks

WordPress has six pre-defined roles, which give the site owner the ability to control what logged-in users can and cannot do within the site. These roles are: Super Admin, Administrator, Editor, Author, Contributor, and Subscriber. We need to make sure our plugin allows the site owner to retain this control.

WordPress provides us a handy little function to check whether the currently logged-in user has a specific role or capability: current_user_can().

if ( current_user_can( 'edit_users' ) ) {
    // The current user can edit other user's information
}

In the above example we check whether the currently logged-in user can modify other users’ information on the site before allowing the ability.

Guard against SQL Injections

If your plugin interacts with the database at all you must escape your SQL queries before the SQL query is executed to prevent SQL Injection attacks. SQL Injection is a very simple exploit attackers use to alter an existing SQL command to expose or override data, and could even be used to execute dangerous system level commands on the web host. Escaping your SQL queries is the solution but it’s very often overlooked by developers.

Here’s an example of code which is susceptible to SQL Injection:

$unsafe_variable = $_POST['comment'];
$wpdb->query( "INSERT INTO wp_comments (comment_content) VALUES ('$unsafe_variable')" );

Why is this bad? We are trusting the content of $_POST['comment'] regardless of what it actually contains! If you see this in a plugin you’ve installed, you are at risk of SQL Injection. Deactivate the plugin and contact the developer regarding this security risk. WordPress provides the $wpdb->prepare() function which allows us to easily escape our SQL queries. Here’s how the code above would look using $wpdb->prepare():

$sql = $wpdb->prepare(
    "INSERT INTO wp_comments (comment_content) VALUES (%s)",
    $_POST['comment']
);
$wpdb->query( $sql );

This code would be protected against SQL Injection. You can read more about protecting queries against SQL Injection attacks on the WordPress Codex.

Prevent Cross-site Scripting (XSS)

Cross-site Scripting is another form of injection attack. It occurs when an attacker is capable of injecting code into the output of a web page which is then executed by the visitor’s web browser. What we learned about SQL Injection is that it’s important to escape data before we save it to our database servers. However, some data — which is escaped and safe to save on our servers — can still be valid HTML, CSS, or JavaScript which could be unsafe to output to our visitor’s browser.

Now we need to sanitize any data we obtain from a user’s input before we output the data. If we don’t, that HTML, CSS, or JavaScript can alter our website and cause problems for our visitors. As you’ve probably guessed, WordPress provides several functions to make this easy.

Say we want to generate a link from data our user has input. Let’s make the text of that link the user’s name and have it point to their website’s url.

What you don’t want to do:

<a href="<?php echo $user_url; ?>"><?php echo $user_name; ?></a>

This is what you want to do:

<a href="<?php echo esc_url( $user_url ); ?>"><?php echo esc_html( $user_name ); ?></a>

Familiarize yourself with the sanitation functions WordPress provides and use them where appropriate.

Prevent Cross-site Request Forgery (CSRF)

Cross-site Request Forgery is an attack that tricks a user into submitting a malicious request. This can happen when a user is logged into a website and clicks a seemingly harmless link (which could have been injected via XSS). This link could cause the user to intentionally make a change to their account (they have authenticated access), and in doing so, they give another person access to it. This is just a single example but CSRF is a common and serious exploit we need to prevent.

To prevent a CSRF attack we need to ensure that an action is actually being performed by the authenticated user rather than an attacker. What we need is some sort of unique identifier which we can verify for a request. We will use a nonce (number used once) as this is a unique identifier.

WordPress provides us with a few functions to use nonces in our plugin. You can add a nonce token to a url using wp_nonce_url() and you can add one to a form using wp_nonce_field(). Anytime a user needs to make an authenticated request, whether by clicking a url or submitting a form, you need to generate a nonce for it. When you use the nonce functions provided by WordPress, a token will be generated on the server side. We will then verify the token’s existence in our plugin before executing the authenticated request.

Here’s a simple example of how to add a nonce to a form:

<form action="/submit-comment/" method="post">
    <input type="text" name="comment">
    <input type="submit">
    <?php wp_nonce_field( 'submit_comment' ); ?>
</form>

When a user fills out this form, the data is sent to the server along with the generated token. We then need to verify this request with the token:

$nonce = $_REQUEST['_wpnonce'];
if ( ! wp_verify_nonce( $nonce, 'submit_comment' ) ) {
    // nonce is invalid!
}

If the nonce is invalid we will halt execution of this malicious request. Why does this work? The nonce token is generated by the server, unique to each user, and is unkown to the attacker. This simple token prevents an attacker from forging a request.

In Closing

We’ve gone through a handful of ways plugins can be exploited by a hacker, but with each exploit WordPress provides a simple solution. Writing plugins for WordPress gives us so much freedom and flexibility on the platform and with this ability comes a responsibility to the community; a responsibility to develop secure plugins that the community can trust and use on their websites. And by doing so, we are making the web a better place.

Posted in Security | Tagged , , | Leave a comment

Blind SQL Injection Vulnerability Found in WordPress SEO Plugin

A blind SQL injection vulnerability has been discovered in the popular WordPress SEO plugin by Yoast. An advisory was issued by the WPScanVulnerability Database after responsibly disclosing the vulnerability to the plugin author:

The latest version at the time of writing (1.7.3.3) has been found to be affected by two authenticated (admin, editor or author user) Blind SQL Injection vulnerabilities.

The authenticated Blind SQL Injection vulnerability can be found within the ‘admin/class-bulk-editor-list-table.php’ file. The orderby and order GET parameters are not sufficiently sanitized before being used within a SQL query.

The latest version of WordPress SEO by Yoast (1.7.4) contains a patch for this vulnerability. The changelog reads: “fixed possible CSRF and blind SQL injection vulnerabilities in bulk editor.

We have scanned all websites with an active VaultPress plan and updated the WordPress SEO plugin to version 1.7.4 where necessary. If there are cases where we’re not able to update the affected plugin ourselves, we will e-mail the website owners to make sure they are aware of this.

If you use the WordPress SEO by Yoast plugin, we strongly recommend that you make sure you are on the latest version. You can update this plugin by visiting Dashboard Updates in your WordPress dashboard, selecting WordPress SEO by Yoast, and clicking the Update Plugins button.

As always, drop us a line if you have any questions or need any help!

Posted in General | Leave a comment

Vulnerability in WP-Slimstat Plugin

A vulnerability has been found by Sucuri in the WP-Slimstat plugin, which affects all versions up to 3.9.5. The vulnerability may allow attackers to inject SQL commands into your database, allowing them to make arbitrary changes.

If you use the WP-Slimstat plugin, we strongly recommend that you update to version 3.9.6 as soon as possible. You can update this plugin by visiting Dashboard  Updates in your WordPress dashboard, selecting WP-Slimstat, and clicking the Update Plugins button.

As always, drop us a line if you have any questions or need any help!

Posted in General | Leave a comment

Remote Access Options on VaultPress

VaultPress will always back up your website, even if it is only connected through the registered plugin on your WordPress website.

However, to really speed things up and take advantage of our automated restore system, we recommend enabling one or more remote access options on the Settings page of your VaultPress dashboard.

In addition to faster backups and restore functionality, providing us with remote access lets us access your server in an emergency, and improve support response times.

VaultPress currently offers the following remote access options:

SSH 

SSH stands for Secure Shell, and it’s a secure way to remotely access a server. SSH provides full access to your server, including your database, so we’ll be able to troubleshoot faster if something goes wrong.

Not all hosts or hosting plans allow SSH access, but if you have SSH credentials we recommend adding them.

Learn more about SSH, and how to enable it for VaultPress.

SFTP

SFTP stands for SSH File Transfer Protocol (or Secure File Transfer Protocol). SFTP allows us to connect to your server securely, but it only provides access to your files, and not your database. We always recommend that you use SSH and SFTP over plain FTP.

Key-based Authentication

VaultPress can connect using SFTP or SSH with key-based authentication, instead of a password. In order to set this up, you have to add the public key shown on the Settings page of your VaultPress dashboard to the .ssh/authorized_keys file on your server. That will allow VaultPress to connect without using a password.

Not all servers will support key-based authentication, so you should contact your hosting provider for further details.

FTP

FTP (File Transfer Protocol) is the most widely-used protocol for accessing files on web server. However, FTP is an insecure protocol that should only be used in limited cases on networks you trust. We do not recommend using plain FTP, if you have SFTP or SSH available to you.

Common questions

Where can I find my remote access credentials?

VaultPress uses the remote access credentials to directly connect to the server where your website is hosted. You should be able to find the credentials by referring to your hosting provider’s documentation, or by asking them. Please refer to this guide for more details, or drop us a line.

Do I need to create a special VaultPress user?

No, VaultPress will work with any username as long as the credentials supplied are valid.

What permissions need to be in place?

The FTP/SFTP/SSH username you configure on VaultPress needs to have full (read/write/execute) permissions over the public folder (usually, “public_html”) on your server.

As always, if you have any further questions, please feel free to drop us a line. We’re happy to help!

Posted in General, Help | Leave a comment

FancyBox for WordPress Vulnerability

A vulnerability has been discovered in most versions of the Fancybox-for-WordPress plugin. This vulnerability makes it possible for attackers to inject malicious code into affected sites. If you’re using this plugin, you should immediate upgrade to the latest version.

Our security scanner has been watching for affected versions of Fancybox-for-WordPress on all VaultPress sites with security plans for the past few days. If you have already received a notification about this, please upgrade the plugin as soon as possible.

As this issue is widespread, we are also manually scanning all VaultPress-protected sites for vulnerable versions of the plugin regardless of your plan level. We will contact affected site owners directly by email, advising you to upgrade.

If your site uses a vulnerable version of Fancybox-for-WordPress, you can upgrade it from your WordPress dashboard:

  1. From your WordPress dashboard, navigate to Dashboard → Updates
  2. Scroll down to the “Plugins” section
  3. Select the “Fancybox-for-WordPress” plugin from the list, and click the “Update Plugins” button.
  4. Wait for the plugin update to download and install.

Alternately, if you are unable to upgrade plugins from your dashboard you can download the latest version of the plugin directly from WordPress.org.

As always, please let us know if you have any questions!

Posted in General | Leave a comment