Secure Your Wordpress Install

Site Hardening Without Pluings

WordPress Developer Security Ninja
Web Security Kung-FU
Cory Marsh
Cory Marsh
Share:
Cory Marsh has over 20 years Internet security experience. He is a lead developer on the BitFire project and regularly releases PHP security and programming videos on BitFire's you tube channel.

Review All Administrator Accounts

Administrator accounts in WordPress are one of the most important security assets your site has. All Administrator level accounts have the ability to create or edit any other user, including creating other administrator accounts. It is very common for compromised websites to have multiple additional administrator accounts created with important sounding names like: super-admin, wp-internal, wp-backup-service, or something similar.

You should perform a thorough audit of all administrator level accounts, if you have any doubt about an administrator account, disable or delete it.

Administrator Account Review

Review And Update Plugins

Plugins with known vulnerabilities are the most common way for WordPress websites to be compromised. Abandoned, old or outdated plugins and themes are more likely to contain security vulnerabilities. If you do not have a security plugin like BitFire installed that monitors this for you, you can use a service like wpscan.com to check your plugins for you.

An alternative to using a plugin vulnerability scanner is to enable Automatic Updates on all of your plugins and themes. This ensures that as security vulnerabilities are discovered and patched, your site stays up to date automatically. This approach does involve some risk that newer versions of themes and plugins may alter the look and feel of your site over time, or even break some features.

Set Strong Password

The number one way that WordPress websites are compromised in 2023 is by far credential stuffing and brute forcing passwords. Credential stuffing is the process of taking valid logins from compromised systems and using those credentials across many different systems to find reused passwords.

This is highly effective since many people use the same password across multiple systems. Automated scanners can submit hundreds of credentials per second to attempt a break in.

Use unique long passwords for all administrator accounts. These should be 12 characters or more and should not be re-used on any other website.

Do not think that just because you run a small website your account will not be targeted. Credential staffers and brute force logins scan the entire internet regardless of domain traffic or reputation. Once you connect to the Internet, your system becomes vulnerable

Add SSL Support

SSL Support is no longer an optional component for your website. In addition to encrypting important security information like login data being transmitted to your website. Encryption also protects private information such as email address and other sensitive data. Adding an SSL certificate also boosts your rankings in popular search engines like google.

If your hosting provider or domain name registrar did not provide you with an SSL certificate, you can esaily obtain a certificate for free from letsencrypt.org. If you are running a higher traffic web site, we highly recommend taking the extra time and money to purchase an Extended Verification SSL certificate from a company like Symantec or your favorite certificate authority.

If you decide to you letsencrypt.org and you have shell access, the process is as simple as installing and running certbot on your webserver. If you do not have shell access, letsencrypt maintains a large list of supported hosting providers with detailed instructions for installing the SSL certificate on your server

FileSystem Security

Lock Protecting Files From the Internet

Review and Update File Permissions

WordPress is very particular about file permissions, and if your hosting provider supplies it, so is CPanel. Linux file permissions are defined for 3 users (owner, group and world) and have 3 basic permission levels (read, write, execute). The execute permission allows program files to be executed, and for directories allows them to be accessed. Each combination of permission values has a unique number. For our purposes we will focus on these.

  • 7 - Read, Write, Execute
  • 6 - Read, Write
  • 5 - Read, Execute
  • 4 - Read
  • 2 - Write
  • 0 - no permission

Directory permissions should be set to 775 or 755 (owner/group: read, write, execute) (world: read, execute). File permissions should be set to 664 or 644 (owner/group: read/write) (world: read).

All files under the WordPress install should be owned by the web user (usually named "www-data") and optionally the group owner may be another group which should also have access to the files (often the primary user account on the Linux server)

You can fix any misconfigured file permissions on your server by executing the following commands from your linux server:

cd /path/to/your/wordpress
find . -type f | xargs chown www-data
find . -type f | xargs chmod 664
find . -type d | xargs chown www-data
find . -type d | xargs chmod 775
						

Remove Backup Files

Nearly every WordPress install I see has some sort of backup files laying around various locations. These backup files if discovered, often contain archived versions of your wp-config file, or worse, database dump. If an attacker is able to access these archived versions of your site, they will have access to your database password, authentication salts, and your hashed administrator password for offline cracking.

A compromised backup will surely lead to a compromised server. If you still wish to keep the backups, move them to a location in your home directory which will prevent them from being downloaded over the Internet

Remove Source Control Files

In addition to the backup files previously mentioned, source control directories like .vscode, .git and .svn contain working copies as well as historical revisions of your code base. Often these files contain passwords and other sensitive data. If you use source control to maintain your website, make sure to keep the code directories in a separate location, or block them with an .htaccess file or Nginx config depending on your web server

Block Access To Private Files

There are several files on your WordPress install that should never be allowed access to. At a minimum your webserver should be configured to block access to all "dot" files (these files begin with a "." character and are hidden by default. These are typically configuration files), you should also block access to ini configuration files, and readme.txt files which can be used to identify active plugins and plugin version numbers.

Sample Apache .htaccess file in WordPress root directory:


RewriteEngine On
# block access to any readme.txt file on the webserver
RewriteRule ^.*/readme\.txt$ - [F]
# block access to any .ini file on the webserver
RewriteRule ^.*\.ini$ - [F]
# block access to any ."dot" file on the webserver
RewriteRule ^\..+$ - [F]

Sample nginx config:
Add this to your nginx server { } block in your nginx config file:

location ~* /(readme\.txt|\.|\.ini$) {
    return 403;
}

Write Lock PHP Files

This step will lock your PHP files from any modifications by malware, and will also lock WordPress from installing any new themes, plugins or upgrading any PHP files on your webserver. This is a great option if you plan to leave the website for an extended period of time without and want to ensure that no PHP files are changed.

It does require super-user privileges (sudo) and so can only be performed on private servers. Shared hosting environments can achieve the same effect in code by using the BitFire RASP Firewall Plugin

cd /path/to/wordpress
# to lock the files:
find -name '*.php' | xargs sudo chattr +i 
# to unlock the files:
find -name '*.php' | xargs sudo chattr -i 

PHP ini configuration

A computer with serious errors melting

Log PHP Errors

Error logging can not only alert you to software problems on your website, it can also alert you to security issues that might be occurring unnoticed. Errors like file permission problems, file access errors and other file not found errors can be an early warning indication of a compromise.

Configuring error logging on a busy WordPress website can be tricky, since many plugins will emit harmless notices about function usage and deprecated features. For most WordPress websites we recommend the following settings in your site /etc/php/fpm/php.ini file or your local /path/to/wordpress/.user.ini file:

WordPress root .user.ini
error_reporting = E_ALL &  ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE
display_errors = Off
log_errors = On
error_log = "/home/your_user/php_error.log"

You should monitor this error log and take action on anything that seems malicious.

If you are noticing too many errors coming from a particular plugin or theme, you can disable error logging for that plugin or theme by creating a .user.ini file in that plugin or theme directory and adding the content:

/wp-content/plugin/noisy-plugin/.user.ini
log_errors = Off

Disable File Uploads

If you know that you will not be uploading files (images, or other files) to your WordPress site with the media editor or other file upload form, you can disable file upload completely. This can prevent plugin upload vulnerabilities from being exploited.

If your site uses uploaded files via the admin page to keep your site updated, you can also use any WordPress security plugin to block just malicious file uploads and achieve a similar effect.

WordPress root .user.ini
file_uploads = Off

Disable Network Access

This may break a few things on your WordPress website. Plugin update checks, news checks, and any plugin that connects to another server will break. However if you are running a static website that is not often updated, this can prevent any malicious file from being remotely downloaded by your web server.

If your webserver has CURL installed, most WordPress functionality will fall back to using the Curl library and so you will not lose any functionality. Some plugins may be effected.

WordPress root .user.ini
# allow url fopen can be disabled for many PHP servers
allow_url_fopen = Off
# allow url include should ALWAYS beb off for EVERY server
allow_url_include = Off

Set Max Post / Upload Size

The default PHP upload sizes should be reduced to as small of values as required to upload images and other files to your website. Uploaded images should be pre-compressed before uploading and minified and be well under the 1 megabyte maximum recommended here.

The maximum input time should also be reduced. Slow inputs can lead to an easy Denial Of Service attack via the SlowLoris Attack WordPress root .user.ini

# size of maximum allowed file upload
upload_max_filesize = 1M
# size of maximum allowed POST request (most be larger than upload_max_filesize)
post_max_size = 1M
# the maximum time to allow requests to upload
max_input_time = 20

Set Max Memory And Execution Time

A default WordPress install should take approximately 100-300 milliseconds to render a web page and uses about 4 Mega-Bytes of memory. As site administrators add additional plugins, themes and configurations these numbers increase. PHP includes limits on maximum script execution time and maximum allowed memory.

Unless you have a plugin or theme that requires an inordinate amount of memory, many WordPress sites can be reduced to 8Mb and a maximum script execution time of 5-10 seconds. Most pages can render in under a single second, however there are background processes that occasionally require more time.

You can check how much memory a php page is using by adding the following line to the end of your index.php file:

Append to WordPress index.php
file_put_contents("memory.log", $_SERVER['REQUEST_URI'] . " " . memory_get_peak_usage() . "\n", FILE_APPEND);

This line will write the number of bytes used to serve the page a line at a time for every page request to the file "memory.log". Once you know a safe value for your server, go ahead and reduce this value in your .user.ini or php.ini file and remove the index.php change.

Append to WordPress index.php
max_execution_time = 10
memory_limit = 12M

Disable Dangerous PHP Functions

Many PHP functions allow executing local system commands or loading unknown extensions and code. These functions can lead to further system compromise if a breech does occur. PHP allows you to restrict some functions from being executed to help mitigate the effect of any breech.

This step is often performed by your hosting provider and requires access to the core php.ini file located in your web server's /etc/php directory. If you have access to configure this php.ini file we recommend the following setting

root php.ini disable functions:

disable_functions =	"apache_child_terminate,apache_get_modules,apache_get_version,\
apache_getenv,apache_note,apache_setenv,disk_free_space,disk_total_space,diskfreespace,\
dl,exec,fastcgi_finish_request,link,opcache_compile_file,opcache_get_configuration,\
opcache_invalidate,opcache_is_script_cached,opcache_reset,passthru,pclose,pcntl_exec,\
popen,posix_getpid,posix_getppid,posix_getpwuid,posix_kill,posix_mkfifo,posix_setegid,\
posix_seteuid,posix_setgid,posix_setpgid,posix_setsid,posix_setuid,posix_uname,\
proc_close,proc_get_status,proc_nice,proc_open,proc_terminate,shell_exec,show_source,\
symlink,system"

				  
Cory Marsh
Cory Marsh
Share:
Cory Marsh has over 20 years Internet security experience. He is a lead developer on the BitFire project and regularly releases PHP security and programming videos on BitFire's you tube channel.