WordPress Security Lockdown
This article is split into two parts for ez reference. First some information on the evil WordPress “Pharma Hack”, and then a recipe for protecting your site with a solid security lockdown. Choose your own adventure:
A few weeks ago, DigWP.com was hit with the so-called Pharma Hack. We discovered the hack after some Google results turned up all sorts of spammy pharmaceutical garbage littered throughout posts, links, and titles.
The tricky part about the hack is that it injects the spam garbage only when your site’s pages are requested by a search bot (e.g., googlebot). So when you view your pages in a browser, everything seems perfectly normal. Put simply, the hack is cloaked. We had no idea anything was wrong until about two weeks after the attack. During that time a majority of our search engine results were nuked with evil pharma spam. Ick.
Flash forward three weeks later and things are locked-down tight. The Pharma Hack has not returned, and most of the spam garbage in the search results has been filtered out and replaced with clean pages. At the time of the attack, DigWP was running WordPress 2.9/3.0 without any sort of additional site security. We were just using whatever “default” protection available from either WordPress or Media Temple. After detecting the hack, several days were spent cleaning it up and locking things down.
At first, it seemed like an impossible hack to fix – nothing seemed to work. We ran through the following routine, hoping to fix it:
- Locate and remove hacked
- Locate and remove hacked content from database
- Replace entire set of salt keys
- Upload new WordPress files
- Restore previous versions of other files
- Restore database to previous version
These actions alleviate the symptoms, but they don’t even touch the actual virus, which somehow regenerates the (base64) encoded spam script. As far as we know, the Pharma Hack works like this:
- Evil script gains access to your WordPress site
- Encoded spam script injected into database
- Script inserts spam garbage into pages requested by search bots
- Script makes no changes to pages requested by browsers
Within the database, the spam script is generated in any/all of these
If these fields are present and contain super-long strings of encoded gibberish, your site’s infected. You can assess the damages by examining the search results for your site (note: other spam keywords may be used):
site:digwp.com cipro OR meridia OR cialis
If you’re hit, hopefully you catch it before googlebot crawls along. But even if you have thousands of hacked pages appearing in the search index, it’s not too late to clean things up and secure your site. Here is how we did it..
WordPress Security Lockdown
This security strategy is best implemented on new sites. It just makes everything (like renaming table prefixes) so much easier. Either way, you want to start with a clean batch of files. Upload a fresh copy of WordPress, update your plugins, theme files, and so on. You may want to redirect visitors to a maintenance page while you work on your site. That said, here is our five-step Security Lockdown for WordPress:
1 – File Permissions
After uploading fresh files, the next step is to ensure proper file permissions. WordPress defaults to
644 for files and
755 permissions for folders. Make sure these are set properly. While cleaning up, we noticed some crazy permission settings for sensitive files. For example,
wp-config.php was set to
777 – executable and writable by the entire world!! Make sure you don’t see anything like that, and if you do, fix it.
2 – File Protection
In addition to setting proper file permissions, we can also lock down key files with
.htaccess. There are numerous files to protect, perhaps most importantly the
wp-config.php file, which contains your database login information. Place the following code in your site’s root
.htaccess file to protect it:
# SECURE WP-CONFIG.PHP <Files wp\-config\.php> Order Deny,Allow Deny from all </Files>
You may also want to password-protect your
wp-admin directory, but it may cause more trouble than it’s worth.
3 – Database Protection
Changing the default table prefix is one of the best ways to protect your database. Malicious scripts need targets, and default targets are easy to hit. Change
wp_ to something more like a password. Some random string like “
crUQZPadESeKSy8Q_” will make your tables difficult to hit. Like having a built-in password for your database :)
There are two ways to change your prefixes: the easy way and the hard way. The easy way is to add the following line to your
wp-config.php file before installing WordPress (important: change the random string to something unique):
$table_prefix = 'crUQZPadESeKSy8Q_'; // custom table prefix
Do that before running the install script and WordPress takes care of the prefix naming automagically when it creates the database. Going forward, there is no reason not to change default prefixes for all future WordPress installs. For existing sites, you can do it the “hard way” using a plugin or doing it manually.
4 – Essential Plugins
After exploring the vast crop of WordPress security plugins, we narrowed it down to four plugins that collectively do just about everything in the easiest way possible:
This plugin tracks changes made to your files. If/when anything changes, it notifies you via Admin Dashboard alert and/or email alert. So anytime a file is changed, moved, added, or removed, WP File Monitor lets you know. Here is a list of features:
- Monitors file system for added/deleted/changed files
- Sends email when a change is detected
- Multiple email formats for alerts
- Admin alert to notify you of changes in case email is not received
- Ability to monitor files for changes based on file hash or timestamp
- Ability to exclude directories from scan
This is one of my favorite plugins. It’s perfect for keeping an eye on things. If anyone gets in and messes around with your files, you’ll know about it immediately, and even better, you’ll know exactly which files have been affected.
This plugin scans your WordPress installation for security vulnerabilities and suggests corrective actions. The scan report informs you of any problems with file permissions, system variables, and much more:
- File permissions
- Database security
- Version hiding
- WordPress admin protection/security
- Removes WP Generator META tag from core code
WP Security Scan also provides a nice summary of server information and latest scan information. Performing a new scan is immediate with the click of a button. Very easy.
Block Bad Queries (BBQ) is a simple, super-fast plugin that protects your site against malicious URL requests. BBQ checks all incoming traffic and quietly blocks bad requests containing nasty stuff like
base64_, and excessively long request-strings. This is a simple yet solid solution for sites that are unable to use a strong .htaccess firewall. Features:
- Checks for updates
- Checks configuration file
- Checks if config file is located in unsecured place
- Checks presence of install script
- Checks server configuration
- Checks database
- Checks code
And quite a bit more. The best part about BBQ is that it’s so easy to use. No setup, no configuration, just plug-&-play. And for even more firewall protection, check out BBQ Pro. Full Disclosure: I am the author of BBQ and BBQ Pro :)
This plugin takes care of all those “little” things. Instead of installing a bunch of smaller plugins or custom functions for this stuff, the Secure WordPress plugin does it all for you:
- Removes error-information on login-page
- Adds index.php plugin-directory (virtual)
- Removes the wp-version, except in admin-area
- Removes Really Simple Discovery
- Removes Windows Live Writer
- Remove core update information for non-admins
- Remove plugin-update information for non-admins
- Remove theme-update information for non-admins
- Hide wp-version in backend-dashboard for non-admins
Having all of this (and much more) done with a few clicks in the WordPress Admin is easy and effective.
5 – Important Details
The previous four steps comprise the majority of our security lockdown, but there are some important details to consider:
- Keep your WordPress install, plugins, themes, and scripts updated with current versions
- Use strong passwords and change them often
- Disable user registration if not needed/used for your site
- Check roles and permissions for all users
- Clean up and consolidate old/loose files
- Remove unused plugins and themes
- Check permissions of
- Keep a backup of your site files
- Keep your database optimized and backed up
We did these things here at DigWP.com, but certain tips may not apply to every site. As a side note, despite our new security lockdown, I am still concerned/confused about how to handle the
backup directories. It seems dangerous to leave these folders set with
777 permissions, and for many shared hosts, that seems to be the required setting. I would be interested in hearing any ideas about securing these directories.
There is no such thing as perfect security. If someone wants in bad enough, they’re going to find a way, despite your best efforts at staying secure. Fortunately, most malicious scripts target the least common denominator, default WordPress installs.
At the very least, ensure proper file permissions, secure
wp-config.php, and use unique database prefixes. Together, these three steps will put your site out of reach for a vast majority of malicious scripts and other automated attacks.
Of course, there are many other ways to strengthen your site’s security, depending on how far you want to go with it. The lockdown strategy presented in this article provides strong security in the most efficient way possible, but there is always room for improvement, so share your ideas and help the community secure their WordPress.
On some shared hosting (Like RackSpace Cloud) they recommend that the last bit in file permissions always be 0, for example, instead of 755 and 644, use 750 and 640.
Great article Jeff!
You’re dead on with your bottom line. There is no such thing as perfect security, the idea is to mitigate risks and keep up to date.
Another helpful plugin is Login Lockdown. Haven’t had any problems yet but it seems to be another good source of protection.
Hi Jeff, excellent article. Here are a few other things to think about and things I’ve done.
– That Google search you did was great (site:digwp.com cipro OR meridia OR cialis), I have the same thing but with a few extra words thrown in there in a Google Alerts notification. Google then checks your site every day for the existence of these kinds of words all automatically and emails you when it finds them.
– As I’m sure you know, new manual installs of WordPress 3.0 let you create a custom database prefix during the set up process. So I’d advise people to make sure and do that.
– In regards as to what to do with a directory like /backup, I personally don’t keep any kinds of backups online, there’s just no reason to. It all rests on an external hard drive. If, however, you need to keep these directories, you can always password protect them. I don’t think WP will have any issues if you password protect a /backup directory. But in reality, that probably won’t do much.
– About the /upload directory. That’s a rough one. The way I see it, you have two options.
1. Don’t use the automatic upload and use FTPS or SFTP to upload images instead (to a more secured folder)
2. Add a little security through obscurity and change the default location of your uploads. So instead of files being uploaded to the /uploads directory, have them uploaded to /blogpics directory.
But you know what Jeff?
There’s one really important part to securing a blog and it’s not a plugin or code of any kind. It’s you… the blogger. WordPress security takes involvement by users. That’s what’s going to help keep the WordPress community as safe as possible.
Remember too, a hacker’s code of conduct is to leave a site easy to hack into by the next guy once they’ve already hacked you. Freak’n jerks!
The ‘virus’ doesn’t regenerate. Generally the way they got in is never locked down and they just keep using that method – over and over again, and they will also leave a backdoor script (most times named something innocuous-sounding like img1.jpg) and use that to get back in after the first hack. You’ve provided great tips, but looking for files that appear to be out of place should also be added to the clean up tasks.
As for your other point, I mention locating and replacing hacked files at least three times in the article.
Even so, both points deserve further emphasis/clarification. Locating any hacked or added files is key to stopping the virus (or whatever it is). During our clean-up, both of us went through every file multiple times. We found only one hacked file containing a backdoor script (in the active theme’s 404.php file). But even after removing it and replacing everything else with fresh files, the database code kept coming back. So finding any hacked files is important, but doesn’t guarantee that the hack has been fixed. So whether the hack occurs through a backdoor, improper file permissions, or direct database injection is not clear. What is clear is that the attack is automated in such a way as to regenerate the encoded database junk repeatedly until things are locked down.
Thanks so much for the info, in particular, the link to the tutorial on manually changing your database prefix. I had previously done just about everything except that, so I dug in and followed the instructions.
Well, lo and behold, as I was making the needed changes in my user_meta table, I came across a lot of mysterious entries. I finally figured out they were left over from some nonkosher admin users created by a hacker a long time ago. I had long ago deleted the hacked entries from the users table, and here I am finally cleaning the last poop out of user_meta. Ah, all gone. What a good feeling.
One more thing. About the permissions on the uploads folder: I’ve found that if you turn off the option to keep your uploads organized by month and year, you can keep the permissions stricter on the uploads folder.
See, if you want your uploads organized by month, WordPress needs to be able to create the folders for the new months. I always turn off this option on new installs.
@Eric Marden – You’re right about how they mask their virus files in other file names like, img1.jpg.
My wife’s site got hacked a couple years ago and I had the hardest time figuring out where the virus was until I finally discovered there was an image file which shouldn’t of been there.
Turned out they zipped up the file twice and named it a .jpg file.
@Jeff – another great plugin out there is the Exploit Scanner and WordPress Firewall.
The point about hack files hidden in images is really important. In my experience hackers will often upload these files using a hacked user account, which makes them look very safe. One trick i found to test uploaded images was to download them to my Mac and view them in “cover flow” in the finder. The stealth zip files had no preview, while real uploads did.
One useful way to protect your uploads folder is to use apache to disable all scripts inside it. The stealth image trick can’t work if PHP isn’t allowed to execute in the uploads folder. I forget the apache declaration but it’s out there.
Very surprised by the omission of the Exploit Scanner. Written by five top WordPress contributors including two of the lead developers.
That’s good to know about… The directory says it’s not compatible with 3.0 and 3 reports of it being broken with it though.
Have just updated with 3.0 hashes. I haven’t been active in Exploit Scanner’s development recently, but have also noticed some potential issues which I’ll try to address at some point soon.
Here is a new article on the Pharma Hack affecting WordPress users, hopefully it helps.
Do you have any server logs that show this activity? Are they compromising WP? A user’s credentials?
From what is known about this hack, they are getting into WordPress using a backdoor of some sort, but the exact mechanism seems to vary from site to site. It would be helpful to examine some server access logs of the actual event – hopefully someone will take the time.
Proper file permissions are, of course, important, but MediaTemple runs PHP as your user, not the webserver user. This means that things like auto-upgrade work out of the box but it also means that ANY script running on your account has full read-write access to your folders – even changing permissions to 444 won’t protect them. This model basically puts all site security squarely in the user’s lap.
@Ken: be careful setting the final permission to 0 – on many hosts this will lock out the webserver (no read access). If 640 does work, then you have the above php-runs-as-me problem.
@Steve, you are so right. I work for a small ISP/web host. Luckily, I teach a WordPress class on a regular basis to interested members of the public, and so I get an opportunity to teach them to stay updated and use good passwords.
Also, our 1-click web app installer (Installatron) is pretty good. It uses random table prefixes, sets up the security keys in wp-config.php, and emails users when an update is available. Still, I send my own email notice to former students. They definitely need the extra nudge!
Educating end users, especially those using click and install packages on hosts, about why security is important is 90% of the battle.
My take is most of these attacks are entering the loop from blogs that have zero security measures. The numbers of those easy targets make me cringe to think about it. Many amateurs(probably millions) believe the host takes care of all that stuff.
I am not sure what changing the prefix to the database tables accomplishes. A hacker could simply call the following to get whatever you change it to.
$table_name = $wpdb->prefix . posts;
Is changing the prefix really going to help?
I guess I don’t understand the purpose of locking down the
wp-config.phpfile in the .htaccess file. The
wp-config.phpfile just defines variables. If a plugin or another hacking software wanted to know the information in that file they could simply request it. For instance if I wanted to know the password then I would just write:
echo "password = " . DB_PASSWORD;
This is also true with the keys and salts in that file.
This is one of the problems with open source programs.
Is it really possible for someone to “simply request” the password or database prefix from any WordPress installation on the Web? To gain access, something needs to be known up front. Locking things down reduces the likelihood that something will gain access in the first place.
They would first have to have access, once a plugin is installed or on the server it has access to everything.
I guess I don’t understand the code you suggested putting in the .htaccess file. How does that lock things up?
It prevents access to the file itself..? .htaccess is a great way to prevent access to sensitive files. Of course, if you assume access, just about any sort of file protection is futile. Locking things down is a cumulative process. A few lines of code is not going to secure your site, but taken as part of an overall strategy it certainly helps.
Nearly every web application on nearly every programming language using nearly every database server needs to store database credentials in plain text. It’s the way it works.
I think what Jason was saying was that with an open source project like WordPress, variables are known among millions of people, where with custom build applications variables are unknown among the visitors to the site.
Trivial? Please explain.
@greg, dumping variables and constants is a trivial task.
I can give you one BIG reason to lock this down with .htaccess, it’s a secondary security feature just in case your permissions are set wrong.
While researching for my book, I was able to find people’s wp-config.php files in clear text viewable online. Now if they had this .htaccess rule as a back up safeguard, I wouldn’t be able to see them.
That’s reason enough for me.
The only reason a php file should display information is if there are echo or print commands. If those don’t exist then the server should render a blank page. If you found a site where the wp-config.php file was displaying all of its information then the server it was on was not set up correctly or they echoed or printed out the variables within that file.
I’ve been searching through all my bookmarks. I thought I saved the Google search to find those. For some reason, I seem to recall seeing this on Perishable Press where Jeff called them “sitting ducks”, but I couldn’t find the post.
Nonetheless, if that code was in their .htaccess files, it should of given a secondary backup as security (or may of). Plus, heck why not. Who knows what new hacks will be coming and if this adds even a little more security, you might as well be ready just in case.
@Jeff Starr – Found a decent article about dealing with 777 folders. Since I’m not the greatest coder on Earth, I’d be interested in your take on this article.
Implementing PHP File Upload Security
@Jason – Ah yes but remember, not every hacker out there thinks doing something like that is “simple”. I’ve had 3 different attempts where hackers tried to use SQL Injection to hack into my database and the string they used they “guessed” that my database table prefix was
It’s not and obviously they just took a crack at guessing it.
So definitely changing your prefix is a great way to help increase security. Is it perfect, of course not. But I’d rather limit how many hackers can hack into my blog as much as possible.
I agree with that, it does cut down on sql injection, and yes every possible step is good, it is just like you said, not perfect.
It is very important to be careful on what you let into your website, ie. plugins. Like you know there are thousands of great plugins, but it just takes one bad one to destroy your site. All plugins have access to every bit of configuration information and database access.
@John: Thanks for the link! Looks like a great article. I’m looking forward to reading it this weekend. Hopefully it will shed some more light on the upload/update/backup directory issue. Thanks for the comments :)
@Jeff – My pleasure. Let me (or us) know if you come across a good, but not perfect, solution for the uploads folder. I’ll keep researching myself. It’s a great topic to cover and research.
Right now the only thing I’ve done is mask which folder is my uploads folder… it’s not named “uploads”.
@Jason – Too bad there wasn’t some kind of “quality assurance” for plugins. Guess they wouldn’t be free then, unless the plugin author was willing to come out of pocket some.
I would much rather pay a little for a good, well maintained plug that was free from hacking, then deal with a uncontrolled broken site.
That being said, I agree there should be a method, maybe with wordpress.org, to assure quality within the plugins.
Your right. Quality assurance would be a good thing.
Great post! I’ve been really driving hard to lock down all my installations since you published this. And I seem to have figured out a lot, well everything is still running, and faster, too (is that even possible?).
Anyway I installed Wp Security Scan, I believe, last week, and I did pretty good (53/56). It gave me a “B” for the location of my wp-config.php. First off I assume it saw my .htaccess file, where it is locked down, hence the “B,” but is there a safer place to put it? Like above the root, say? Is it even possible to move it elsewhere, in relation to where WordPress is installed?
Yes, you can move the wp-config.php file up one level, but that was not designed as a security measure. Indeed it provides no real protection. (Its purpose is for SVN externals.)
wp-config.phpmay not protect against a determined hacker, but it will certainly deter a majority of scripted attacks that target the file in its default location.
@Andrew: I think I see. Interesting.
@Jeff: That answers my question to Andrew’s response. Thanks.
Hey folks! Just wanted to drop in here and share the following (mt) Wiki article: Recovering from a site compromise. It’s quite thorough and should provide you with all of the tools you might need.
Editor’s note: Media Temple removed the article mentioned in this comment. Link removed.
Hey, great article. I really appreciate all the work and effort everyone is putting into helping to make WP more secure and viable as a total web platform. I’m learning much more about web security than I ever really wanted to. But it’s all good.