After thoroughly scrubbing the WordPress database, I think I’m ready for the next step, the scariest, which is to recreate the blog entirely with a brand new installation.
So that’s what I’m going to do.
I’m going to:
- Back up the database one final time.
- Create a new database and import the backup into that one – new database, new user, new password, etc.
- Record the names of all the plugins was using – recorded to a tracking document in Google Docs (or wherever would work for you).
- Record the important settings for the plugins used – also recorded to the tracking document.
- Back up the files that make up the compromised WordPress install – highly recommended – you need this to get your uploaded media back, as well as any banners you may have customized for your favorite theme – if you restore, though, try to restore only media files from the backup – be aware you may have backed up malware.
- Delete the compromised WordPress installed files by removing every file from that subdirectory – I used Dreamhost’s control panel because the original install had been via Dreamhost’s control panel, but you could use an FTP client or SSH session to do the same sort of thing.
- Create a new WordPress install (newest version) – again used Dreamhost’s one-click install – easy peasy – created its own database which I later deleted.
- Delete all themes and plugins – these are filesystem based with settings living in the database. If you delete a plugin your database thinks you’re using or a theme you are using, you’ll get an error that appears once and the plugin won’t work until you reinstall and with a theme you’ll just go to the default theme until you select another. Turns out to be easier to delete plugins in the WordPress Admin UI but easier to delete themes on the filesystem with SSH or SFTP.
- Hook your new install up to the restored database – Requires editing the /wp-config.php file. Be sure to update DB_NAME (line 20), DB_USER, DB_PASSWORD, DB_HOST and be sure the value of the $table_prefix variable (line 62 of the standard version of wp-config.php) is right too. When I did it I forgot the $table_prefix variable and ended up with an extra set of tables with a semi-random prefix.
- Make sure that works – Click around. I failed to do this and ended up noticing that my missing .htaccess file was screwing up all but the home page.
- Do some cleanup – I used this opportunity to remove the extra set of database tables from my forgotten $table_prefix setting above as well as purging some tables from plugins I didn’t intend to reinstall. The term for this operation in MySQL and other databases is “drop” if you’re looking for it.
- Install the plugins and themes you want from known good sources – a known good source is, for instance, the Add New function within the WordPress UI. You could also download and install directly from authors you trust – but if you do that I’d recommend using md5 checksums to make sure you’re avoiding malware. Not that anything is foolproof.
- Evaluate new plugins – Your WordPress install may have included some other plugins that looked interesting or you might be interested in other plugins you’ve heard about. Now’s the time.
- Restore header images from old backup – I copied header images from the old backup to the new installed theme.
- Restore upload media from old backup – I copied uploaded media from the old backup to /wp-content/uploads/ – had to do this carefully because there was a 2011 subdirectory that I didn’t want to obliterate.
- Restore .htaccess file from old backup – I looked at the .htaccess file carefully to make sure there wasn’t any sign of malware and then transferred it to the install directory of the working WordPress install. This fixed the 404 errors I missed out on when I didn’t click around up above.
- New final scrubbed state backups – I made 3 backups: (a) the files from the install on the host’s filesystem, (b) a full WordPress Export (XML) and (c) a full database backup (SQL)
- Carry on and hope I’m continuing hack-free
Wish me luck!
P.S. if you make any comments from between the time I do a database backup and the time I restore it, they’ll be lost. So maybe don’t wish me luck.
Update: Okay, you can talk now. Transition to new install is complete. Also I am updating the procedure to reflect real-life tasks and troubleshooting.
Now to do the same scrubbing with the professional blog.
Update 2: I did some additional work to harden my install. Thought I should note it here along with related recommendations:
- The default admin account: I deleted mine because I have other users who are administrators, but the standard recommendation is to rename your administrator account. You can do this with a SQL query or if you are working on setting up a new blog, just don’t use the default “admin” name. Lots of hack scripts assume that name and do not do digging into your install to try to figure out who else might be an admin. For more info on renaming the admin account for an existing blog, try this link. Note that for this you need to be okay with SQL operations and you have to know your WordPress table prefix.
- Admin versus Author/Editor roles: If you publish a lot of posts on your blog and you happen to be posting with an account that is an admin, that’s bad. The editor account you post with usually has its username plastered all over your blog which means that account is a high risk target for brute force hacking attempts. If that user is an admin and it is compromised then your whole blog is compromised. It’s more of a pain for you but more secure to split up the roles. For administration operations (using the entire dashboard, adding plugins, changing settings, etc.), have a dedicated user with a non-obvious, non-published username. For posting, use an account that is locked down to being able to create and edit posts (assign Editor or Author roles – experiment, just be safe).
- Change the table prefix in your database: A hacker may be able to identify your WordPress database host name and database names but may not know the table names. It’s possible to brute force attack anything and the more obscured the data about your setup the better. The standard WordPress database table prefix is “wp_”. That means all the table names in your database start with wp_. The hackers know this and may run attacks designed on this knowledge. It’s possible to change the table name prefix default. You can do this while creating your WordPress or you can do it with SQL commands after the fact. For more information on this operation try here. NOTE: This one’s a little tricky. Be sure not to miss the last UPDATE statements or you’ll be locked out.
- Security-related Plugins: I am using a few – The principle of most of these is not to fix the identified (potential) issues but just to call them out. It’s important to know that the potential for false positives is high, so you have to be comfortable evaluating risks yourself and figuring out what risks you’re willing to assume. Also consider installing, using and uninstalling to reduce the “surface area” for a future attack. It can be fiddly but is arguably better security protocol. The best security-related plugins so far seem to be:
- Antivirus: I installed this during the hack forensics and then uninstalled again, but it worked well and did as intended.
- Exploit Scanner: Another good one for forensics. Install/use/uninstall if you’re uncomfortable with just having it installed.
- SI CAPTCHA Anti-Spam: Uses a CAPTCHA at login to try to stifle brute force login attempts. Also provides other security, anti-spam and accessibility features.
- WP Security Scan: Provides recommendations, hides the WordPress version number and has other features.
- Regular backups: You can backup the WP installation from your filesystem if you have FTP access to your host. You don’t have to do the entire directory but you should at least back up your /wp-content/uploads/ directory and any themes you might have modified (or added header images to). You can export your posts and comments easily from WordPress (if you have an Administrator account) and you can and should backup your database. If you don’t have access to the database directly or are uncomfortable with the tools you have access to there, you can install plugins to help with the database backup. A solid, quick and dirty plugin is WP-DB-Backup, but others are also available that will schedule the backups and e-mail them to you or slap them into a DropBox or something. Also your hosting provider might provide backups as a regular part of their service. Do take regular backups and store them someplace securely.
- Hide your WordPress version: This is another way to foil hackers’ automated scripts. If it’s hard for them to know your version it’s harder for their automated tools to attack your installation. Various plugins provide this service (see above).
- Move or hide your wp-config.php file: The wp-config.php file has lots of secret information in it and it lives in an internet browsable location by default. You can use an .htaccess file to restrict access to the file, you can sometimes move it out of the browsable directories entirely, or you can move the secret information out of harm’s way. These operations may require you to reapply the change (depending on how you do it) after each WordPress upgrade. For more information on how to do this, try this link. An even more geeky treatment is here.
- Sign up for Google Webmaster tools: These tools will alert you if your site structure changes. Does not require an XML site map to sign up for the tools, but I think the site structure change alert probably does require a site map. Looks like these folks are relatively non-scammy. My blog exceeds their 500 page free limit, though, so I may pay their $20 for the full version.
- Run site audit tools from trusted sources: This is fraught (with security risks) and I’m still trying to ID a reliable tool/service. Will update this post when I do.