Thank you for visiting the Rackspace Community
The The Community is live! Post new content or topics so our teams can assist.

Please contact your support team if you have a question or need assistance for any Rackspace products, services, or articles.

Wordpress Security Best Practices (Linux)

  • Wordpress is a great CMS system, especially for brand new bloggers, or those with little coding knowledge. However, with the number of installations out there, Wordpress has become a big target for attackers. The good news is that there are many steps that can be easily taken to harden your Wordpress Installation.

    Linux Users and Permissions

    It's very common for those new to Wordpress to set their permissions wide open (777) as soon as they see a Permission Denied error from Wordpress. When you do this, it allows any user (most importantly the web server process) the ability to modify the files in your wordpress install. To lock this down, I recommend that you create one user for each wordpress install, as the FTP User for the site. We will assume that you have a single site, and name this server "wp-user". To create this user:

    sudo useradd wp-user -d /home/wp-user -m -s /bin/false

    Two things of note: The user does not have a shell (there is no reason for this user to log in to a shell) and we did not set a password for the user. This is as I normally recommend SSH Key access only. If you plan on using password authentication for FTP, you will need to create this password (make sure its strong).

    Setting Permissions

    Normally, best practice is for the document root of your site to be owned by a user other than the web service's system user, and to deny write permissions to the web service. The web service only needs read permission to serve content, and giving write or execute permission only leaves an attack vector to the outside world. Unfortunately, as Wordpress needs the ability to upload files, and update it's own code, we have to bend these rules slightly. We will start with ownership. Ownership for the entire directory should be:

    wp-user:www-data

    This means that 'wp-user' has user ownership, and 'www-data' (the system user for the apache web server) has group ownership. Depending on your operating system, this user may also be named 'httpd' or 'apache'. If you are using nginx, the user will be 'nginx'. To accomplish this, you can run:

    sudo chown -R wp-user:www-data /var/www/example.com/

    Base permissions for your wordpress install should be:


    755 (drwxr-xr-x) for folders

    644 (-rw-r--r--) for files

    This means that the wp-user will be able to modify anything, but the web server has read-only access. To accomplsh this you can run this, with the assumption that the document root for your site is /var/www/example.com/:

    find /var/www/example.com/ -type d -exec sudo chmod  755 {} \;
    find /var/www/example.com/ -type f -exec sudo chmod 644 {} \;

    This means that the wp-user will be able to modify anything, but the web server has read-only access. This is common practice for static sites, but as we discussed, there are some files wordpress will need to be able to access and execute in order to function correctly. These are the exceptions, assuming the same document root:

    find /var/www/example.com/wp-content/uploads -type d -exec sudo chmod 775 {} \;
    find /var/www/example.com/wp-content/upgrade -type d -exec sudo chmod 775 {} \;
    find /var/www/example.com/wp-content/themes -type d -exec sudo chmod 775 {} \;
    find /var/www/example.com/wp-content/plugins -type d -exec sudo chmod 775 {} \;
    
    find /var/www/example.com/wp-content/uploads -type f -exec sudo chmod 664 {} \;
    find /var/www/example.com/wp-content/upgrade -type f -exec sudo chmod 664 {} \;
    find /var/www/example.com/wp-content/themes -type f -exec sudo chmod 664 {} \;
    find /var/www/example.com/wp-content/plugins -type f -exec sudo chmod 664 {} \;
    
    sudo chmod 775 /var/www/example.com/wp-config.php

    These directories are used for wordpress updates, theme and plugin updates, and blog attachment uploads (most commonly images).

    Wordpress Admin User

    Just as your linux installation comes with a "root" user, your Wordpress installation comes with an "admin" user. And the admin user is plagued by the same issue, when attackers attempt to brute force, this is an administrative user that exists in most every install. The easiest way to close this attack vector is to remove the "admin" user. You can easily create a user with a different name, give them administrator priveleges, and then delete the "admin" user.

    Secure Updates

    FTP is inherently insecure, especially when using password authentication. It is much more secure to set-up password-less ssh-key updates.First we will need to make sure that we have the proper packages installed. On Ubuntu or Debian, this can be done by issuing the following:

    sudo apt-get update; sudo apt-get install php5-dev libssh2-php libssh2-1-dev

    After this we will set up our SSH access. We will need to do these steps as 'wp-user'; since we disallowed login as wp-user, we will need to open a shell with the sudo command:

    sudo -u wp-user /bin/bash
    

    After this, you can move to the wp-user home directory and set up SSH keys:

    cd ~
    ssh-keygen -t rsa -b 4096
    mkdir ~/.ssh; cd ~/.ssh
    echo 'from="127.0.0.1"' `cat ~/.ssh/id_rsa.pub` > authorized_keys
    exit

    After this make sure permissions are set correctly:

    sudo chmod 700 /home/wp-user/.ssh
    sudo chmod 040 /home/wp-user/.ssh/*
    sudo chmod 644 /home/wp-user/.ssh/authorized_keys

    After this, just add the following to your /var/www/example.com/wp-config.php

    define('FTP_PUBKEY','/home/wp-user/id_rsa.pub');
    define('FTP_PRIVKEY','/home/wp-user/id_rsa');
    define('FTP_USER','wp-user');
    define('FTP_PASS','');
    define('FTP_HOST','127.0.0.1:22');

    And you should be able to update wordpress, plugins, and themes without being prompted for login information.

    Plugins

    Best Practice is to use as few plugins as possible to achieve your desired result. There a few that I recommend to promote security:

    • Login Security Solution - This is an all in one plugin that provides the ability to set strict password requirements, set password expiration periods, and get email notifications for repeated failed logins
    • Disable XML-RPC - While it is possible to lock down XML-RPC via an .htaccess file, unless you have a compelling reason to need remote control of your wordpress install, it is better to simply disable it to prevent pingback attacks.
    • Disqus - The built in user and comment system for wordpress is decent, but it is very prone to spam. As such, I usually disable open registration ("Settings > "General" > uncheck "Anyone can register"), and use Disqus to moderate comments, and allow users to authenticate against their facebook or google accounts.

    Moving Foward

    These settings should prevent the majority of possible attacks, but to really lock down your set-up you can look at:

    • WP Fail2Ban - Use this plugin to integrate Wordpress with Fail2Ban!
    • XML-RPC Access - Use an .htaccess file to allow XML-RPC only from accepted sources.
  • Nice article, although I think there are a series of errors within the stated commands, I've certainly struggled to follow them and can't get it to work as intended.

    • The ssh key gen seems to be missing a space, prior to creating the dir
    • The line after this is trying to create a file named "authrozied_keysexit", which I think is a mistake too
    • Another space/return missing when chmod'ing the ssh files
    • From research the WP definition seems to be FTP_PRIVKEY rather than FTP_PRIKEY

    I'll keep trying to get this working.

  • Hello citepaul!


    I'm sorry that you're having trouble making it work! I have edited the above article, it appears that Syntax Highlighter had broken the formatting on the ssh-keygen section. The correct formatting is as follows:

    cd ~
    ssh-keygen -t rsa -b 4096
    mkdir ~/.ssh; cd ~/.ssh
    echo 'from="127.0.0.1"' `cat ~/.ssh/id_rsa.pub` > authorized_keys
    exit

    However, I looked again at my configuration file, and I definitely have "FTP_PRIKEY" and "FTP_PUBKEY" in my working setup. I hope that this helps! This article was written from my experiences locking down my own wordpress configuration, so I am glad to assist in anyway that I can!

    Thanks,

    Russell T.


    EDIT: Sorry, I just noticed the formatting error in the permissions section. The commands should read:

    sudo chmod 700 /home/wp-user/.ssh
    sudo chmod 040 /home/wp-user/.ssh/*
    sudo chmod 644 /home/wp-user/.ssh/authorized_keys
  • Hi Russel,

    Thanks for replying, I'll give this another go when I get a moment.

    I think you're right and it is FTP_PRIVKEY, however I believe the paths may be missing the ".ssh" folder in your example here?

  • Hello citepaul,


    Thanks for getting back to me. In your wp-config.php, you should be referringdirectly to the wp_users home directory, not the .ssh directory. The reason behind this is that openssh requires the .ssh folder to have 700 permissions, and the authorized_keys file to have 600 permissions. This means that only wp_user can read that file.


    Wordpress runs on php, which means that the linux user which controls wordpress will be the apache user. as the .ssh directory is only accessible by wp_user, we need to duplicate the keys elsewhere, in a directory with at least 755 permissions. In this particular example, I chose /home/wp_user/ for no other reason than ease. Any other directory with at least 755 permissions would work just as nicely.


    Additionally, I looked into the key specification for wp-config.php, and according to the wordpress documentation, the correct value is "FTP_PRIKEY" (no V):

    http://codex.wordpress.org/Editing_wp-config.php#WordPress_Upgrade_Constants


    Hope this helps!

    Thanks Again,
    Russell T.

  • Hi Russel,

    Thanks again for getting back to me, I'm still struggling with this unfortunately, here's where I'm at ...

    1.

    ssh-keygen -t rsa -b 4096

    When running the above I 'enter' my way through the options. The first of which is where to save the key, I'm running CentOS and it places the keys in .ssh by default.

    Is that correct? Only the next line in your example is to make the .ssh directory, which has already been created in my experience.

    2.

    In your most recent response you mention that the key paths within wp-config.php are indeed correct without the .ssh directory. In that case do I need to copy the created keys from the .ssh directory to somewhere else at some stage then? This isn't mentioned in the guide.

    If so at what stage do I need to do that? You also mention this needing to be a directory with 755 permissions. In your example is the wp-user home directory set to 755 then? Is that secure? What permissions do the copied key files need?

    Sorry for the questions, I'm just really keen to get this working on our set up.

    Thanks, Paul

  • Hello Paul,

    CentOS is fairly unique in this behavior. Most other distributions (my particular example runs on debian) do not create this directory by default, and will drop the keys into whatever your present working directory is. Since you are using CentOS, you will need to make sure, manually, that the proper files are in the proper places. That is to say:

    1 copy of the public key at /home/wp_user/.ssh/authorized_keys with permissions set to 600

    1 copy of the public key at /home/wp_user/id_rsa.pub with permissions set at 644

    1 copy of the private key at /home/wp_user/id_rsa with permissions set at 644. Your home directory should be set to 755, and .ssh should be set to 700.

    And then your wp-config.php file can point directly to the home directory.

    You asked also about the safety of the home directory being set to 755. This is actually the default in most linux distributions (It may not be in CentOS, I'm unsure on that account). If this gives you pause, you can use a different directory, it just needs to be readable by the system user running apache.


    Hope this helps,


    Russell T.

  • Hi Russel,

    Thanks for the update, I'm very close to getting this working now.

    I can now get an update to begin installing, but unfortunately it errors with the following:

    "Unable to locate WordPress Content directory (wp-content)."

    I've done some research which hint at permissions. I'm pretty sure I've everything set as per your article though. Any idea at all?

    Paul