Cleaning Up Wordpress: Lessons Learned in Website Security
Tags: wordpress, apache2, htaccess, security
Published: 2019-09-21 19:13:03

---
title: Cleaning Up Wordpress: Lessons Learned in Website Security
published: true
tags: [wordpress, apache2, htaccess, security]
canonical_url: https://www.wesleylhandy.net/blog/wordpress-security-lessons-learned.html
---

[![Someone holding a laptop with 'You've been hacked!' displayed upon the screen](https://www.wesleylhandy.net/static/66c5a6070912c2b67175cdce5e6880a3/c35de/hacked.jpg "Someone holding a laptop with 'You've been hacked!' displayed upon the screen")](/static/66c5a6070912c2b67175cdce5e6880a3/c35de/hacked.jpg)

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 advertisments 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:

## <svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>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:

  - [https://www.upwork.com/](https://www.upwork.com/)

**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.

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](https://wordpress.org/plugins/miniorange-2-factor-authentication/).

Links:

  - [https://www.getastra.com/blog/911/how-to-fix-wp-vcd-backdoor-hack-in-wordpress-functions-php/](https://www.getastra.com/blog/911/how-to-fix-wp-vcd-backdoor-hack-in-wordpress-functions-php/)
  - [https://stackoverflow.com/questions/46219263/php-code-in-functions-php-of-all-wordpress-websites-on-my-shared-hosting](https://stackoverflow.com/questions/46219263/php-code-in-functions-php-of-all-wordpress-websites-on-my-shared-hosting)
  - [https://labs.sucuri.net/wp-vcd-malware-comes-with-nulled-themes/](https://labs.sucuri.net/wp-vcd-malware-comes-with-nulled-themes/)
  - [https://blog.sucuri.net/2016/05/nulled-wordpress-themes-malvertising-black-hat-seo.html](https://blog.sucuri.net/2016/05/nulled-wordpress-themes-malvertising-black-hat-seo.html)

**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](https://www.digitalocean.com/docs/droplets/how-to/add-ssh-keys/create-with-openssh/), 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:

  - [https://www.smashingmagazine.com/2014/05/proper-wordpress-filesystem-permissions-ownerships/](https://www.smashingmagazine.com/2014/05/proper-wordpress-filesystem-permissions-ownerships/)
  - [https://wordpress.org/support/article/hardening-wordpress/#changing-file-permissions](https://wordpress.org/support/article/hardening-wordpress/#changing-file-permissions)

**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:

  - [https://docs.sucuri.net/warnings/hardening/](https://docs.sucuri.net/warnings/hardening/)
  - [https://geekflare.com/wordpress-x-frame-options-httponly-cookie/](https://geekflare.com/wordpress-x-frame-options-httponly-cookie/)
  - [https://wordpress.stackexchange.com/questions/175436/cookie-set-without-httponly-flag](https://wordpress.stackexchange.com/questions/175436/cookie-set-without-httponly-flag)
  - [https://geekflare.com/http-header-implementation/](https://geekflare.com/http-header-implementation/)

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

Links:

  - [https://wordpress.org/plugins/limit-login-attempts/](https://wordpress.org/plugins/limit-login-attempts/)
  - [https://wordpress.org/plugins/sucuri-scanner/](https://wordpress.org/plugins/sucuri-scanner/)

## <svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>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.