Wesley L. Handy, Full Stack Web DeveloperThe professional portfolio and website of Full Stack Web Developer, Wesley L. Handy. Wesley loves all things JavaScript, React, Node, Gatsby, and enjoys working with databases like MongoDB, Firebase, and MySql.

September 20, 2019

Cleaning Up Wordpress: Lessons Learned in Website Security

Someone holding a laptop with 'You've been hacked!' displayed upon the screen

You've been working on a project for months, you've handed the keys over to the client, and then you get that dreaded email...

... on page [x] some users have been experiencing weird advertisements and popups. I think the site has been hacked! I don't know what to do. Please help! ...

I faced this situation recently. I volunteered to finish up a project for a nonprofit organization (not the one where I work 9 -5). They were one year into a new site build when the developer they originally hired ghosted them.

I'm a JavaScript guy, so I wasn't all that familiar with the entirety of the Wordpress ecosystem, but I figured I knew enough, I had been building my own plugin for work to inject a React application via a shortcode, thinking, "How hard can it be? It's not even real development..." (don't get offended, just expressing what I was thinking at the time).

I was very aware of security issues in a Node.js environment, but I had always relied on other services to take care of hosting security. I had heard of .htaccess files, but I'd never put my own LAMP stack application into production from scratch. I hadn't even given it any thought since I have been focusing more and on tech like React, Gatsby, React Native, and even a little Angular.

As easy (in terms of difficulty) it was to handle all the front-end requests of getting the site finished to the client's expectation, I took for granted my own ignorance of the rest of what was involved. I want to share what I have learned over the past two weeks so that (1) I won't forget next time, and (2) so you can avoid many of the mistakes I made.

I am going to try and provide links to all the sources that have helped my in my recent journeys and to briefly talk through some of the more important parts. Let's call this:

Wordpress Security 101

  1. Always Hire a Developer You Either Know or Can Verifiably Trust

    It is very tempting to hire the cheapest developer you can find. But sometimes you get what you pay for.

    I like that there are sites where developers can be rated, or public reviews left, like Upwork or LinkedIn (want to suggest others?). These help provide accountability for the developer and some sense of security for the client. In this particular case, the original developer took a low fee for a quite complex site and cut a bunch of corners.

    Links:

  2. Never download and use unlocked or nulled themes - pay the regular fee from a reputable theme marketplace. Otherwise, you may install Malware!!!

    I didn't even question the use of the theme on the client's site. Knowing the client, I assumed they would have purchased the theme themselves but it turns out they left it to the developer, who perhaps out of ignorance or a willingness to cut corners downloaded a theme online that was corrupted with the wp_vcd malware.

    I only discovered how long the malware had been there by comparing the current site with versions of the codebase that existed while the site was offline or in maintenance mode.

    This particular Malware was found within functions.php, and it created three files within wp-includes: wp-feed.php, wp-vcd.php, wp-tmp.php. It wrapped pages with malvertising scripts and also added a script that logged user's IP addresses who had permissions to edit the site and systematically ignored those users, so they would never detect the malicious behavior when browsing the site themselves, whether logged in or not, whether browsing privately or not.

    After logging into the remote server as an administrator-level user, I found the odd looking files at first within the wp-includes folder. I removed them, thinking I had solved the problem. The Malware was creating redirects to sites with the oclaserver domain and logging IP addresses to wp-feed.php. I used the following command-line script to find these files.

    grep -Ril "oclaserver"
    • grep searches files for lines matching a given pattern.
    • R makes the search recursive, looking through all files and directories within the current director
    • i ignores case
    • l suppresses the output to only list the filename

    Initially, I though this solved the problem, but the next time I checked, the files where back. I deleted the files, then checked again, immediately, the files were once again there, full of the same content. This means either my wpdb was corrupted, or some other file that gets called repeatedly by Wordpress was corrupted, or possibly both.

    What did I do next?

    Before scouring through my DB in phpMyAdmin, I first identified and deleted all unused themes and plugins. The files kept appearing. So I started searching for terms within the files.

    What found me the culprit was that within wp-temp the code was setting a password variable. My only thought is that this was a hash of the admin password (so I made a note to change the admin password after cleaning up). I ran the same grep command but searched instead for that password value.

    Lo and behold, the functions.php of the parent theme lit up. There, I found a bunch of hacky code that was wrapping all the pages with the malicious script and systematically ignoring the IPs where an admin user had been logged in.

    After deleting the code, I deleted the malicious files from within wp-includes and the files didn't return. I wasn't yet certain the Malware was completely gone because I noticed that the code in wp-temp was creating a cookie on the client.

    Before changing the admin password, I reset the SALT Keys within the wp-config.php file using https://api.wordpress.org/secret-key/1.1/salt/.

    Then, I cleared the cache and cookies from my browser, and logged into the wp-admin of the site from an incognito window. Only then did I reset the admin password.

    Finally, I created a new admin user for myself and encouraged the client to add 2FA for their login via the Google Authenticator Plugin.

    Links:

  3. On your server, add a new user and remove root login to make it harder for someone to hack your server.

    Since the client already had their site installed and running on Ubuntu 18.04 a Digital Ocean droplet, the previous developer had ssh access to the droplet. I really didn't want them to be able to log back in, whether or not they had nefarious intent. I noticed there was only one root user on the system, so I created a new user for myself:

    sudo adduser myuniqueusername

    I then added my new user to the sudo user group:

    sudo usermod -aG sudo myuniqueusername

    And then I added administrative privileges to myuniqueusername:

    sudo visudo

    Within /etc/sudoers:

    myuniqueusername ALL=(ALL:ALL) ALL

    Finally, within /etc/ssh/sshd_config:

    PermitRootLogin no

    Then from command-line, restart ssh:

    sudo service ssh restart

    From there, do whatever is necessary to create ssh keys for your new account, set those up, logout, and then ssh back in as you.

  4. Harden file permissions on the server to prevent unwarranted changes. From within the root directory of your Wordpress installation (perhaps /var/www/html/) do the following:

    Find what groups your user and your server belongs to and make sure that your user has ownership of all your Wordpress files.

    sudo groups
    sudo usermod -aG groupname myuniqueusername
    sudo find . -exec chown myuniqueusername:groupname {} +

    Change files to only be writable by owner (you), and only readable by others, Change directories to be only be created, modified, or deleted by you or Wordpress, and prevent anyone other than you or Wordpress from reading or writing to wp-config.

    sudo find . -type f -exec chmod 664 {} +
    sudo find . -type d -exec chmod 775 {} +
    sudo chmod 660 wp-config.php

    Links:

  5. Add hardening to your httpd.conf Apache configuration by disabling Server banners, hardening your .htaccess file, by disabling directory listing, disabling HTTP Trace, preventing MIME sniffing, preventing clickjacking, preventing some forms of cross-site scripting, and securing your cookies within wp-config.php.

    Within /etc/apache2/httpd.conf or /etc/apache2/apache2.conf:

    ServerSignature Off
    ServerTokens Prod

    Then from command-line:

    sudo service apache2 restart

    Within .htaccess, insert between # BEGIN Wordpress and # END Wordpress:

    Options -Indexes
    RewriteEngine On 
    RewriteCond %{REQUEST_METHOD} ^TRACE 
    RewriteRule .* - [F]
    Header set X-Content-Type-Options nosniff
    Header always append X-Frame-Options SAMEORIGIN
    Header set X-XSS-Protection "1; mode=block"

    Within wp-config.php, add to the end:

    @ini_set('session.cookie_httponly', true);
    @ini_set('session.cookie_secure', true);
    @ini_set('session.use_only_cookies', true);

    There is some discussion over whether or not the above method is best, you could and perhaps should also add cookie hardening to your apache server configuration, see links below.

    Links:

  6. Add Security Plugins like Sucuri Sanner and Limit Login Attempts.

    Links:

Final Thoughts

The list above is by no means exhaustive, and it's just based on things I've been learning the past couple of weeks. I am very open to correction, addition, or subtraction, from the above by anyone with more experience. While I don't plan on focusing primarily on Wordpress, I'm learning more and more LA of the LAMP stack through this experience, and at the end of the day, with my interest in Gatsby, I probably will be involved more and more with the ecosystem.

That being said, there is nothing wrong with learning more about website security.

Even if static sites are the future, keeping our CMS and web-servers safe should not become lost or remain assumed knowledge.

Wesley L. Handy

Written by Wesley L. Handy who lives and works in Virginia Beach, VA, finding ways to build cool stuff in Gatsby and React. You should follow him on Twitter