False Security
Credit: Grafitti Verite
I wrote this months before the botnet attack of April 2013, but I kept putting off posting it. Clearly now is the time! So since people often ask me if I do certain things to protect my site, here’s what I don’t and do do.

What I don’t do

• Hide the WP version in my HTML
• Remove readme.html
• Hide login error messages
• IP blocking*
• Use a different prefix for your DB
• Move wp-config.php*

I don’t bother with the readme or the WP version because it doesn’t matter. People don’t actually search for ‘Who’s using WP 3.4.2? I’ll attack them!’ They let slip their dogs of kiddie cracker war and bury us in traffic. I learned that lesson with the TimThumb debacle. My server got slaughtered by people not searching for TimThumb, but slinging attacks at me as if I had it installed! Even better? They didn’t bother to differentiate where my install where WP was in a subfolder (domain.com/wp/) and just attacked domain.com/wp-content/themes directly. The same thing happened with the recent botnet attacks. Basically people are going to attack me, assuming I’m vulnerable. It’s only when I’ve pissed someone off directly that I’d worry about having a specific version being an issue. And since I keep up to date with upgrades and patches, I don’t worry so much at all.

The error messages thing stems from people worrying that failed logins to WP will tell you that you got the username or password wrong. So if I login as Lpstenu, it’ll say ‘ERROR: Incorrect username.’ That apparently spooks people, thinking that if you know that you’ve gotten a right username, you’ll hammer that. Do me a favor. Go to yourdomain.com/?author=1 and what happens?(This doesn’t work on this domain because I created it back when WP defaulted your first user to ‘admin.’ I made a second ID and deleted that one.) That’s how much effort it takes to find your username, folks. It’s even easier when you look at this post and see the author name, and a link to it, right there in front of you. Your username isn’t a secret. It’s dead easy to get. I’m not wasting time hiding something that easy to find.

That’s not really a valid “security” improvement, anyway. It’s irrelevant whether the attacker knows what he got wrong, as it provides no extra information that would help him to get in. Furthermore, the usernames are exposed in dozens of other places already as I showed you before. I often argue that you can’t remove doors: everyone has to be able to get into a house, so we put locks on our doors as deterrents, and signs up to say we’re watched by ATD or whomever. All of those can be circumvented, and you still have a door. Most crime is prevented by deterrents, however (a sufficiently motivated and skilled person will work around anything), so really all we do is make things inconvenient enough that they go somewhere else.

Locking CablesPart of security is knowing where to spend your time. Make a better mousetrap and you get smarter mice, true, but if you still want to get rid of the mice where do you start? I start with not hiding the obvious. Here’s my username, here’s my login location. They’re standard on most websites, because people have to be able to log in. Now when I really have a locked down site where I want no one but me to log in, I use .htaccess to limit login to just my IPs. This is a (minor) problem when I’m on the road, but I can always SSH in to fix that. Most of the time, though, I trust in my firewall, my server, and the basic security of WP to be enough.

IP blocking is totally useless to me. With a caveat. I use CSF and ModSecurity on my server which will block by IP if you hit very specific abuse parameters, including my newer ModSec rules for protecting logins. However I don’t pay much attention to it, save to whitelist my commonly used IPs. The point of the firewall is not to stop people I know are bad, but to dynamically catch them in the act, block them on the fly, and then let that IP gracefully expire after a certain amount of time. Years ago I may have had to use .htaccess for that, manually updating it to block specific IPs, but software’s come a long way, and letting the right tool do that job is huge. If you only have .htaccess, well, you can use some .htaccess protection of logins, or you can use Perishable Press’s 5G Blacklist. As I tell people frequently, you never know where legit traffic is coming from, don’t be foolhardy.(True story. A customer at work insisted he did too know better, and blocked China and India traffic. Then he went there on vacation and was pissed he couldn’t log in. Yes, I mentioned I had warned him before.)

Curiously controversially, I don’t mess with the DB prefix. I use wp_ much for the same reason I never move my wp-content folder unless I’m using CDN (and even then…) : Poorly written plugins and themes will kill me, and people can view my source code or use DB insertion calls in their code. They don’t have to know my prefix, and in fact, best coding practices are intended to work no matter where the folder is or what you use as a prefix. The other reason is I’m exceptionally lazy, and the less I have to remember that I did ‘differently’ in case of an emergency, the easier my life is. This is important when I’m ever hacked (yes, when), because I can restore faster from scratch if I didn’t go nuts reinventing the wheels or moving things around. Rebuilding a wp-config.php is very easy if I only have to change passwords and user IDs, after all.

Similarly, I don’t move my wp-config.php in most cases. I do on my localhost instance (so I can wipe the folder and DB and start over easily), but really it’s impractical in other situations for me. I think it would be safer to move it out of a web-accessible folder, and when possible I do that (sometimes I have WP in a subfolder) but I have other things I can do to protect that file.

What I Do

Besides a massive amount of work keeping my server up to date and tuning my firewall, I do some things that anyone using WordPress can do:

Stupid Security

• .htaccess protect wp-config.php
• Lock file permissions
• Prevent plugins from writing to wp-config.php and .htaccess
• Prevent folder content browsing (for images mostly, but also plugins)
• Use strong passwords for WP/FTP/SQL accounts
• Use one-time passwords for WP/SQL/FTP/SSH accounts

I protect my wp-config.php from direct access with a really simple .htaccess directive:

<files wp-config.php>
order allow,deny
deny from all

I think nginx is this:

location ~* wp-config.php { 
    deny all; 

This means you can’t see https://halfelf.org/wp-config.php in your browser. It’s pretty minor, in so far as things go.

I lock down my file permissions as tight as I possibly can. Nothing is set to 777, and my .htaccess isn’t writable. This means if I use a plugin that wants to edit my .htaccess (or wp-config), I have to do it manually. This is good, in my opinion. I always know exactly what I’m doing. In my .htaccess I also have Options -Indexes, which stops people from being able to browse empty folders (this is important for plugins that don’t have an index.php file). Since I’m using SVN and Git, I also prevent people from seeing those:

	RewriteRule ^(.*/)?(\.svn|\.git)/ - [F,L]
	ErrorDocument 403 "Access Forbidden"

My passwords are stupid complex. I haven’t the foggiest idea what they are thanks to 1Password. I also don’t reuse passwords. This is very important for how my server is setup, as DSO requires you to enter in passwords to upgrade WordPress. While I can use my main account, I actually created an FTP only account for each and every website on my server, and then I hard coded that (and it’s password) into my wp-config file. So yes, I have a DB password (each account is used once for each DB) and an FTP password (again, one account for each account) in my config. And no, I’m not worried about that. Sometimes I have a generic SQL ID for all DBs under one account, though that’s a tiny bit more risky.

But, most importantly, I try to cure myself of being stupid. I don’t log in to my site via non-secure ways (SSH & SFTP only). The passwords I use for my login (which is not SSL protected on WP) are one-account/one-use. I try never to log in on someone else’s computer. I don’t do admin work on potentially unsafe wifi. You see, the greatest security risk in the world isn’t the software you’re using, it’s you. You do stupid things, like recite your credit card info (or password) over your cellphone while on a train trying to get your host to reboot a server. You use Starbucks’s wifi to pay your bills. You talk about how your mother changed her name.

Social engineering is way more dangerous than any server hack, and when it’s down to the wire, that’s what I’m more worried about. After all, I have good backups of my files.

Reader Interactions


  1. love this. thanks for providing your great techniques for others!

  2. This is a really good article. You correctly point out what is wrong with the WordPress security plugins. They all give a false sense of security.

    There is 1 thing that I disagree with though:

    > Use a different prefix for your DB

    I think you should do this. It can greatly reduce the possibility of a SQL injection attack. If there is such a vulnerability in some plugin or theme, the different database prefix will make the job of the hacker extremely difficult.

    • Changing the DB prefix doesn’t matter because the majority of WP plugins don’t use wp_ as a defined prefix anyway. Actually, we won’t approve a plugin that does. The injection fear is not that someone will use a plugin and put in a long string of code with the prefix in there. We have, and use, wpdb to handle a prefix that may be anywhere.

      It’s the same thing as moving your wp-content folder. If WP can still access it, so can the cracker. The security is not in where your DB is but how the data is, or is not, sanitized.


    • Changing prefixes used to be a practical way to stop a lot of scripted SQLIi attacks, even though security-by-security isn’t real security. Good to know plugin developers and the repository have adapted to make prefixes less relevant, but if sanitization fails, a known prefix (whether it’s wp_ or developer-specific) will help make a successful SQLi exploit. That’s why I’ll probably always cling to the habits of security by obscurity methods. It’s semi-superstitious. For the one odd attack that relies on the assumption of a default, not using the default drops its chance of success to zero.

    • I haven’t seen any SQL injections that specified the prefix in a year or so, FWIW.

    • I’m not surprised. Like I said, it’s superstition and habit. 🙂

    • The entire database prefix thing became an issue back in 2010 when MT and GoDaddy were hacked. The attacker penetrated the environment and developed scripts to attack everyone with default wp_ prefixes. Everyone that had it was screwed.

      It doesn’t protect you any more or less from outside attacks, unless your database ports are open to the world.


    • I’d forgotten about that debacle, thanks Tony!

      Those were pure DB attacks, though, rather than injections via WP. And if your server is that vulnerable, ain’t not much that you can do to WP to save yourself.

  3. Thanks. Now that I think about it, you are right. This too will be security through obscurity.

  4. I think your advice is spot on, a false sense of security seems to deter people actually learning the why and the how. The problem as you say is the people, and there are a lot of them that do not update WP/Plugins and choose bad passwords (or download malware straight off of Google).

    One reason to change the prefix is for encapsulation, much like a large boat has compartments to prevent breeches from effecting other areas of the ship, it is a valid and precautionary measure for both protection and monitoring. Each area you close off enables you to have more control over the water’s route.

    ‘People don’t actually search for ‘Who’s using WP 3.4.2? I’ll attack them Who’s using WP 3.4.2? I’ll attack them!”

    This isn’t true, there are several WordPress scanners, some build right into metasploit that do version fingerprinting for both WP and Plugins. If you have an outdated WP or plugin you can be assured that this will be bumped up the target list, and shared/sold. Large Botnets on the other hand don’t really care, but not every attack is one.

    Exploit scanners also fingerprint your username, thus guessing 1/2 of your login, this is a real security issue that is unable to be fixed without changes to core (there is a trac ticket about it). There is no reason WP should expose your login name. Period. Luckily 2-factor Authentication can help in this situation and there are a couple plugins that enable this.

    I also think people using SSH should disable password/login’s completely and instead use keys. That vast majority of attacks on a server are going to pound your ports and not necessarily 80.

    • People are not scanners, though I agree that certain SCANNERS are written to exploit known versions, it’s a little different than, say, a scanner that is built to only attack WP 3.4.2. No one writes a hack script to target just one version. The script may run different attacks based on what version they find, but they’re not specifically written for a version. The ones I’ve seen tend to have ‘if 3.4, try this, if 3.3 do this’ and so on. It’s a semantic I’ll agree is a bit fine though 😉

      I also think people using SSH should disable password/login’s completely and instead use keys. That vast majority of attacks on a server are going to pound your ports and not necessarily 80.

      Yes! I wrote how to do that here – https://halfelf.org/2012/passwordless-ssh/ and yes, yes you should 🙂

  5. As the article says the botnet has gathered a range of ip’s to initiate attacks from, so ip lockout plugins are not necessarily very useful… 1 thing we have implemented is to block access to the admin in the .htaccess file, and sepcify specific ip’s for access within it.

  6. Hi Mika, thanks for your sound advice and no nonsense thinking (as usual)

    You need to check your css… Below a width of around 1140px your background is made up off image: xbg.jpg.pagespeed.ic.9wrOYmQsmm.jpg (which is very dark grey of sorts) because your inner-cs.jpg file is not loaded (says loading failed when checking wih firebug) resulting in a page that is practically unreadable on mobile devices.
    ps. just delete this comment after you’ve fixed it 🙂

  7. I got myself a US$5/month VPS and installed a VPN on it. Now I have both a dedicated IP-address I can use for white-listing even when on the road, and also an encrypted tunnel when on insecure WiFis.

    You should also never send any login over an unsecured connection, which means you should use SSL for your admin. Don’t use a self signed, as they make you more open to man-in-the-middle attacks, but the very cheapest ones from any OK provider will do. You can get them from as low as US$8/year, and the extra security is totally worth it.

    Also two-factor authentication makes the login extremely secure. I use Duo Security which is very easy to use and free for personal use.

    • Sadly, it’s a motherfucking nightmare, and way too expensive, for me to run SSL certs for logins here, since I’m using multisite with mapped domains. Also there are a lot of domains on this site, so I would have to spend a lot for everyone to have a cert per domain.

      I do have a cert on the subdomain used to manage email and the cpanel stuff.

  8. Most of your do’s are done by default with Better WP Security.

    • My issue with using a plugin for that is that I no longer know exactly know what I’ve done, which means if there’s a problem, I don’t know what to un-do. Mind you I’m more jaded about this plugin than normal, since I’ve seen an uptick of people who have NO idea how to do anything, and they keep installing and mis-configuring it, and then complaining to me at DreamHost.

      • Prevent plugins from writing to wp-config.php and .htaccess

      Specifically, I know Better WP does exactly that. It writes your .htacces and changes it so nothing else can, which is great until you need to fix it and don’t know how to use chmod.

      It’s at a weird spot, where I don’t think it’s as newbie friendly as it should be, but if you know what to do you don’t need to use it.

      And I don’t like their .htaccess rules 😉 But that’s just a me thing. They feel like overkill.

  9. Hi Mika

    You make a lot of good statements in this post, but you also make a lot of bad ones.


    You’re right here: My server got slaughtered by people not searching for TimThumb, but slinging attacks at me as if I had it installed! – it’s the shotgun approach, throw it and see what sticks


    You’re wrong here: People don’t actually search for ‘Who’s using WP 3.4.2? I’ll attack them!’ – I know about 2 dozen script kiddies that will disagree with you.

    For the most part you’re correct, opportunistic attacks won’t leverage this, but targeted attacks will leverage everything and anything they have in their arsenal. It’s easy to forget targeted attacks, because opportunistic ones are so prevalen and most rarely get impacted by them. Note that I don’t disagree with your Don’ts, just your descriptions on why.. 🙂

    But in term of it being on your dont’s, same page…

    I actually agree with most of yours:

    Hide the WP version in my HTML
    Remove readme.html
    Hide login error messages
    IP blocking*
    Use a different prefix for your DB
    Move wp-config.php*

    With exception to IP whitelisting, but maybe that’s not what you meant. I do caution against your thoughts on why you don’t hide login error messages:


    That apparently spooks people, thinking that if you know that you’ve gotten a right username, you’ll hammer that.

    The login error for the username is only one thing. The reason they say to do that is because most of the brute force tools look for the default errors to identify failure from success. Successes get flagged and the attacker can proceed, such a tool is Hyrdra for remote brute forcing. You designate the success so the system knows when to stop.

    That being said…

    the act of removing it is a bit useless to me as well. I prefer to whitelist and add layers of defense on top of it. In short I focus on successive POST attempts against my wp-admin, that’s if they can even get to it with their IPs….

    This I do agree with:

    IP blocking is totally useless to me.

    They rotate IP’s too fast, switch your mindset to a whitelist approach. Deny all, allow few, versus the opposite..

    I agree with all your do’s:

    • .htaccess protect wp-config.php
    • Lock file permissions
    • Prevent plugins from writing to wp-config.php and .htaccess
    • Prevent folder content browsing (for images mostly, but also plugins)
    • Use strong passwords for WP/FTP/SQL accounts
    • Use one-time passwords for WP/SQL/FTP/SSH accounts

    The only one I’d encourage you to consider is Two- Factor / Multi-Factor authentication on your admin. It’s about layered defenses. You never put all your eggs on one basket, and we already know that access and vulnerabilities are the leading issues, so what’s the real harm in putting a little extra layer on the access..

    By far my favorite comment is – Part of security is knowing where to spend your time.



    • I think I expressed myself poorly when I said that that I don’t worry about people aiming for specific versions of WP, looking back at it. But it’s exactly that reason you mentioned why I don’t spend too much time worrying about it. If they’re throwing everything they’ve got at me, from 3.3 vulnerabilities to 3.6-beta, then what version I have is less of an issue than that I have the software. Mind you, we all are only using the latest, most secure versions of software, right? 😛

      Good point about the login errors. I too focus on successive attempts 🙂 but I hadn’t thought about scripting like that. Funny, cause I used to write it!

      Two-Factor logins is … ugh. I really love the idea, and yet I hate it. Google has it, but not that long ago I was in a dead-zone, no phone at all, and I wanted to login and send an email (yes, I have a Google GMAIL account for … reasons). No way for me to get SMS, no handy list of backup codes. FML. The problem with it is that to make it sufficiently accessible for all situations, or even just common ones (dead zones and airplanes), you have to introduce more risks. The annoyance factor is higher than the security benefits right now.

    • Googles 2 factor auth does not require network access. I can explain the math if you’re interested. 🙂

    • … Goddamn it, Otto, now I have to actually look at it.

    • Well, there you go then. 🙂

      Google’s Authenticator app implements RFC 6238, which is a time based variant of RFC 4226, which is the HMAC based one-time-password algorithm.

      To put it shortly, it’s like this:
      – Generate a secret random number. Put it on the device and store it somewhere on the service in question.
      – Have the device calculate this:
      $counter = floor( time() / 30 );
      $code = hash_hmac( ‘sha1’,$counter, $secret );
      – Then fiddle with $code a bit by taking the least significant digit, and using it as an offset to grab 4 bytes out of the middle of the the 40 bytes of $code.
      – Do a little more fiddling around and turn this into a 6 digit number that’s easy to display and type in.
      – Display that number on the device, and have the service do the same calculation to verify that the number is correct, given the same secret and time. To make it easier, do the calculation both before and after the current 30 second time window, so that the user can be off by a minute and still have it verify properly.

      So essentially, no communication between the service and the device is required. The device just runs a mathematical calculation to get a number, the service recognizes that number. The end. The only trick is making them both have the same number, and that’s where the shared secret comes in.

      The service generates a shared secret. To get it on the device, it converts it to base32 (don’t ask), then generates a QR code from that, which you scan with the device. Hey, a legit use for QR codes! 🙂

      All that’s left is making sure that both the service and the device think it’s the same time, and with modern clocks and auto syncing and everything else, this isn’t usually a problem.

      This is essentially the same thing that a “SecureID” does in a hardware form, but in a software form and re-programmable. Also, you don’t have to pay $50+ per SecureID FOB device. You can have multiple services in the app, each with their own secret code and thus with their own constantly rotating identifier. Dreamhost supports it on their control panel. Works fine. You should try it. I have it enabled for that, and gmail, and half a dozen other services. Handy and secure.

    • I’m trying it out now, as it happens, and there’s an article for the DH site about Better Passwords Damnit! in the works 😉 All of which is related.

      I still have issues with 2FA, mind you. If I lose my phone, I’m totally fucked for a lot of things.

      (The plugin is interesting on Multisite, it’s the same QR for all the sites on the network…)

    • Multisite shares a single unified user-base, and since the shared secret is per user, then it will have the same code for all the sites. No different than all the site sharing the same password, really.

      But different users should get different codes.

    • With Duo Security that I mentioned earlier, you can do your authentication in multiple ways: With an app on your smartphone, pregenerated passcodes (for offline use), phone verfication, hardware tokens (even 3rd party) etc. An overview of available authentication methods can be found here: https://www.duosecurity.com/authentication-methods

  10. great article and comments! I”d be interested to learn more about your VPN setup Bjorn, will you write about it?
    Also, if you want to hide your author archives there are several techniques here

    I like to redirect to my about page

    • Well Paul, since you asked, I just wrote down a small howto (there are probably better ones out there if you Google, but: “This is my howto. There are many like it, but this one is mine.”):

      As for the actual WordPress protection part, you would do something similar to what Mika did with wp-config.php. You just protect wp-login.php (and perhaps the whole /wp-admin/, except for admin-ajax.php) instead and insert an “allow from “/”allow ” above the deny statements. I’ll write a howto, but not tonight.

    • Great stuff! But I am curious — is this about all that a cheap VPS is good for, or am I unfairly prejudiced against them?

    • That entirely depends on what you want to do with your VPS. The main drawback to the cheap ones is that you have a lack of support for when y screw up. I don’t know if you’re prejudiced, since all you’ve said gives us nothing to base that comment on.

      Cheap hosting is cheap hosting. A $5/month VPS for a VPN and other costs for other servers for other things… or a $100/month VPS for everything….

      Take your pick.

  11. The problem with yourdomain.com/?author=1 can easily be solved using a nice plugin.


    Greats from Germany

    • Or I can redirect it with .htaccess, but why? Author pages are valuable methods of displaying content in a way that helps your SEO (why do you think Google ranks page authors?)

%d bloggers like this: