WordPress runs on PHP, and is a core component to pay attention to when hardening your WordPress site. This article will cover some of the most common, low-hanging fruit you can address when it comes to PHP security for WordPress.
Heads up – Be careful when making changes to your PHP settings. Incorrect settings and syntax may damage your website. Always test your changes in a development or staging environment before making changes in production.
Table of contents
Use the latest PHP version
In addition to ensuring you’re patching your operating system, MySQL server, web server (e.g. Nginx, Apache, IIS…) it’s also absolutely crucial to ensure you’re running a fully supported version of PHP ((https://www.php.net/supported-versions.php)).
Since PHP is exposed to the Internet (because WordPress runs on top of the PHP interpreter) it is at greater risk of attack in the event that an exploit for a vulnerability within PHP is discovered. To such an extent, keeping PHP up to date (and more so, to stay away from the end-of-life 5.x PHP versions) is crucial not only to prevent attacks from occurring, but also from allowing attackers to escalate attacks if they manage to gain a foothold into your WordPress website (e.g. by exploiting a vulnerability in a WordPress plugin).
Suppress the PHP version
Like most web server software, by default, PHP exposes the version it is running via an X-Powered-By HTTP header. While this in and of itself is not a security vulnerability, like in any other situations, enumerating software versions is generally useful for attackers during the reconnaissance phase of an attack. Therefore, by disabling the PHP version, you’d be making an attack a tiny-bit harder to succeed.
Fortunately, this is easy to disable using the expose_php setting. In your php.ini configuration file, simply add the following.
expose_php = Off
Remove any phpinfo() files
Creating a PHP file using the phpinfo() function to use for debugging is a common bad habit many PHP system administrators have. Should an attacker come across a ‘phpinfo’ file (usually, unsurprisingly named, phpinfo.php), they are given a wealth of information and details about not only the version of PHP running on the server, but also other aspects of the website such as the web and database server.
If you really need to run phpinfo(), it is much more preferable to do so from the command line, as follows.
If you have more than one configuration of PHP on your system (very common), you can use the following command to specify which php.ini file to use.
php -c /etc/php/apache2/php.ini -i
Suppress PHP errors and warnings
While errors, warnings, and exceptions are helpful during development, if displayed in a public-facing environment, attackers will often use this information in order to gain insight into the server configuration, application layout, and components.
Error messages are some of the most common paths to information disclosure, often leaking information such as application installation path and database connectivity details. Instead of showing errors and warnings, ensure that this information is logged. More information about this topic may be found in the official PHP documentation.
Luckily, this is easy to disable using the display_errors setting. In your php.ini configuration file, simply add the following.
display_errors = Off log_errors = On
File inclusion vulnerabilities allow an attacker to control a PHP include() statement. While PHP will execute any PHP code within a file that gets included, it will print out anything else (assuming it’s plaintext). This means that an attacker taking control of a file inclusion vulnerability may end up running something similar to the following, giving them access to sensitive system files. To read more about file inclusion, take a look at this article.
By setting the open_basedir setting in php.ini, you can instruct PHP to only allow includes within a certain directory and below. While this does not eliminate file inclusion vulnerabilities, it certainly makes them more restricted, and also twarths some more advanced attacks which may lead to code execution (when an attacker can execute commands on your server).
You can set the open_basedir PHP setting as follows in your php.ini file.
open_basedir = /var/www/html/example.com
Disable remote file includes
In addition to restricting local includes to a specific directory using open_basedir, it is also advisable to disable remote includes. Remote file inclusion (RFI) attacks work similarly to local file inclusion attacks, but instead of an attacker being bound to files on the system, remote file inclusion allows an attacker to include files over the network. This is extremely dangerous, and most RFI attacks end up in an attacker being able to arbitrarily execute code on your web server (referred to as remote code execution, or, RCE).
In order to disable remote file includes, use the allow_url_fopen and allow_url_include PHP options in your php.ini file as follows.
allow_url_fopen = Off allow_url_include = Off
Disable or limit dangerous functions
In the event that an attacker, despite all your security measures, successfully manages to find a vulnerability in the PHP security of your WordPress, which they can exploit, the last thing you want is for them to be able to run arbitrary code on your server. If an attacker is able to run arbitrary code on your server, they can likely install a webshell or establish a reverse shell to further control your server and use it to do their nefarious bidding (like spreading malware, using your website for phishing campaigns or denial of service attacks, or even mining cryptocurrency).
By disabling functions such as shell_exec() and system(), you can prevent users and attackers from making use of these dangerous functions. While there may be legitimate reasons to make use of these functions, they are very few and far in between, and there is usually a more secure way to achieve the same result.
Heads up – Test the following thoroughly in a testing or staging environment before running this in production, as some software/plugins you are running may (hopefully legitimately) depend on these functions.
The following is a collection of potentially dangerous functions you may wish to disable in PHP using the disable_functions setting in your php.ini file.
disable_functions = php_uname, getmyuid, getmypid, passthru, leak, listen, diskfreespace, tmpfile, link, ignore_user_abord, shell_exec, dl, exec, system, highlight_file, source, show_source, fpaththru, virtual, posix_ctermid, posix_getcwd, posix_getegid, posix_geteuid, posix_getgid, posix_getgrgid, posix_getgrnam, posix_getgroups, posix_getlogin, posix_getpgid, posix_getpgrp, posix_getpid, posix_getppid, posix_getpwnam, posix_getpwuid, posix_getrlimit, posix_getsid, posix_getuid, posix_isatty, posix_kill, posix_mkfifo, posix_setegid, posix_seteuid, posix_setgid, posix_setpgid, posix_setsid, posix_setuid, posix_times, posix_ttyname, posix_uname, proc_open, proc_close, proc_get_status, proc_nice, proc_terminate, phpinfo&amp;amp;amp;amp;lt;/em&amp;amp;amp;amp;gt;
Block PHP execution in sensitive directories
By default, your wp-content/uploads directory, which is used to upload themes, plugins, images, videos and other files to your website is allowed to execute PHP code. This is a fair bit risky – if an attacker is somehow able to take advantage of a file upload vulnerability in a plugin or a theme, they may be able to upload a webshell or a similar kind of backdoor. Since PHP execution would be allowed within the wp-content/uploads directory, that attacker may be able to actually execute that PHP code to do their malicious bidding.
Preventing this is actually quite simple – disable PHP execution within the wp-content/uploads and the directory. The naive way would be to simply block access to *.php files like so using .htaccess.
&amp;amp;amp;amp;lt;Files *.php&amp;amp;amp;amp;gt; deny from all &amp;amp;amp;amp;lt;/Files&amp;amp;amp;amp;gt;
However, the above configuration is relatively easy to bypass – PHP will in most cases gladly execute .phtml .php3 files. The most bullet-proof way would be to literally disable the PHP engine within the directory. The following .htaccess configuration should be placed within the wp-content/uploads and the wp-includes (another directory commonly targeted by attackers) directory.
php_flag engine off
PHP security for WordPress – final thoughts
While WordPress itself is to be considered a robust and secure platform, the environment and infrastructure your WordPress installation runs on goes a long way to ensure the safety of your website as a whole. Implementing the topics covered in this PHP security hardening guide, will provide you with a second line of defense, making it significantly harder for an attacker to escalate an attack after abusing a vulnerability within your WordPress website.
If you want to learn more about how to harden your WordPress site, read our definitive guide on WordPress security & hardening.