Security in WordPress plugin development

What my ultimate goal is, other plugins be it infected(backdoor etc) or not cannot access the username/password/mail of my email, that is the main part of the question.

The only way to be sure of this is to not store them in a manner than can be automatically decrypted in the database/filesystem – which is to say, not store them in plain-text or an automatically decryptable fashion at all.

I’d recommend concatenating/hashing your own security salts and keys with those configured for WordPress itself – you should never distribute a plugin that uses hard-coded salts as anyone with the plugin is basically handed the keys to whatever’s been hashed for every installation. By adding in the WordPress installation’s own salts, should you discover that your plugin is vulnerable you can update the salts and invalidate credential stores of all installations when the plugin update is pushed. If an individual installation becomes compromised, a user can change their WordPress salts and likewise invalidate their credentials without altering the plugin.

Can other plugins access my plugin code? e.g. require("../plugins/mail/class.client.php') and call new Client()?

Yes.

If so, […] why is this allowed?

It’s not “allowed” so much as it is a consequence of interpreted scripting environments. “Extending WordPress” with themes and plugins is just that – it’s modifying WordPress’s execution using raw source to achieve the desired outcome. I know of no interpreted language that actually provides reliable mechanisms to “lock” portions of source-script from other portions within the same code-base. Operating systems, sure.

Rather this responsibility falls onto the execution environment and whoever controls it.

How do I prevent it?

It may be possible to achieve using some seriously complex sand-boxing and running different portions of WordPress in different threads – but this is not at all how WordPress is designed, and would take heavy environment re-configuration to boot. Even then, it would still be susceptible to some variety of attack – despite Google Chrome’s sandboxed JavaScript, malicious code still emerges capable of defeating the measures.

As with all information security, it should be assumed that if the environment is compromised, so too is everything in and on it, regardless of security measures in place. Even binary viruses in sandboxed environments and those executed in virtual operating systems can break out of their confines to harm the host system, if designed with such scenarios in mind.

If the consumers of your plugin fail to properly secure their WordPress installations or install compromised code, there is no portion of the filesystem or database that can be considered “safe” for critical information to reside. The same as if a virus finds it’s way onto your computer, no program or document can be trusted to remain unadulterated or unexposed – if you store passwords in a plain-text file, you must assume that all relevant accounts are compromised or vulnerable. Here as everywhere else in the IT security world, users should not install executables that they do not trust or understand – those who do not have a good basis to differentiate between trustworthy or well-implemented programs should have a limited ability to acquire and/or install such items.

In short, if you yourself do not directly control the WordPress installations on which your plugin is installed, you cannot prevent your plugin from becoming compromised beyond following good security practices in your implementation.

Can other plugin read the contents of the file as I’m storing the encryption key in Client class?

Yes. In fact, any plugin could parse the WordPress installation’s wp-config.php file and obtain the database information and salts should it’s author desire. The thing is, a plugin needn’t even do that to compromise an installation as the PHP and WordPress environment already give plugins what amounts to free-reign over the database and filesystem to achieve their ends, insofar as the database and filesystem themselves permit. Without these abilities, plugins would be extremely limited in scope.

If so can you recommend a place to store that so that only Client() class of this plugin can access this?

No. Credentials stored and/or transmitted as plain-text are never completely “safe”, period.

If your application requires such storage/transmission, then the credentials can only be as safe as the environment in which they’re stored. You can add additional hoops to obfuscate the credentials, but the fact remains that if your code has everything it needs to decrypt the credentials, then so too does any other code within the environment. It is essentially the same as storing an encrypted file containing passwords on your computer alongside a shell script that decrypts the file as soon as executed. The best way to keep them safe is to secure the environment.

I’m using current_user_can() and nonces but is this enough for add sufficient security to AJAX?

It depends on the specifics of your application and what’s being transmitted via AJAX. Generally, these mechanisms when implemented properly are sufficient to validate that AJAX requests are being made from a consistent source. However, they in and of themselves are only a component of AJAX security. As general rules of thumb:

  • Do not take any action until the request is “sufficiently” verified, including the use of current-user_can(), check_ajax_referer()
  • Escape, validate, and sanitize all the things that comprise dynamic data. These practices are imperative to prevent SQL injections, arbitrary PHP execution, and delivering malicious JavaScript to visitors. This includes $_GET, $_POST, and $_COOKIE data as well as anything retrieved from the database that will be evaluated as PHP or sent to the browser. Learn which WordPress functions handle the tasks for you – but when in doubt, perform them yourself until you can be sure.
  • If you control the server environment, Secure your site with an SSL certificate and operate over the HTTPS protocol in order mitigate the chances of man-in-the-middle attacks (the Let’s Encrypt project is well on it’s way to offering trustworthy certificates for free).
  • Ensure that your error-handling is implemented in such a manner that if your server-script chokes or is accessed directly by a client, no sensitive data is spat back to the browser.

Can nonces be expired ?

Yes – WordPress provides the nonce_lifetime filter for this purpose. By default a WordPress nonce is valid for 12-24 hours.

Can multisite create any additions problems from my scenario?

It depends on how your plugin is installed and you intend for it to be used in a multisite environment – the potential “problems” that could arise might actually be desire-able outcomes depending on what you’re gunning for.

You may want to store mail credentials as user or site-specific data depending on who should have access to which mail – you may even want to implement custom roles and capabilities to provide finer-grained access to mailboxes.

Leave a Comment