Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: security

  • Evaluating Evil

    Evaluating Evil

    Credit: EvalBlog
    Credit: EvalBlog
    One of the things I do at DreamHost is help with hacked sites. This means when WP is hacked, I look at it, figure out how, and explain to the person how to fix it, or how to tell their tech folks what needs doing. There are occasions where I’ll delete things for them, but usually that happens when there’s a folder or file with weird permissions.

    We have a lot of tricks with what we look for, like base64, but recently I started to find files that missed my scan, but not my “Hey, wait, wp-mai1.php isn’t a WordPress file…” check. Files like this:

    $a51a0e6bb0e53a=str_rot13('tmhapbzcerff');$a51a0e6bb0e5e4=str_rot13(strrev('rqbp rq_46rfno'));
    eval($a51a0e6bb0e53a($a51a0e6bb0e5e4('eF6dWFtv6kYQ/it9qMQ5UlWBCVGtKg+JWozQaSrcU9+qKvIlBIGh6BBCyK/[...]')));
    eval($a51a0e6bb0e53a($a51a0e6bb0e5e4('eF7tW1uvotqW/ivnYSe1d85JignSvcxJPXgDtQCXKNdO50TAJSqop7xw6fR/7zHm1CUqqGW91EMnK1FgzHG/[...]')));
    

    Now obviously I can just add str_rot13 to my checklist (nothing in WordPress core uses it), but .. how do I look for those eval strings?

    Eval is a funny thing. In JavaScript: The Good Parts, Douglas Crockford states “eval is Evil: The eval function is the most misused feature of JavaScript. Avoid it” but he’s taking JS and I’m looking at php files. So with the (current) assumption that I can ignore js I can try this(I also use ack for this half the time, depends on my mood)(You can leave out ‘exclude SVN’ stuff if you want to. Most users don’t have it.):

    grep -R --exclude-dir="\.svn" --exclude="*.js" "eval" .
    

    That gets me a lot of files, though, and I don’t want to parse what I don’t need to. By the way, there’s one and only one file in all of WP that uses eval() in a ‘nefarious’ way, and that’s ./wp-admin/js/revisions-js.php, which is the WordPress easter egg. That’s also the only place you’ll see p,a,c,k,e,r code. But clearly I want to look for eval( or even eval($ because that’s more exact, and that should give me a better result.

    This is a two edged sword, of course. If I’m too precise, I will miss some of their shenanigans. If I’m not close enough to what I’m looking for, I get too much. And worst of all, I don’t always know what I’m looking for. Quite a lot of finding new hacks is a world where “I’ll know it when I see it.” So let’s take it down and say I want to find no JS, nothing in .svn, and anything with eval and a paren:

    grep -R --exclude-dir="\.svn" --exclude="*.js" -e 'eval(' .
    

    That’s a lot better, and in fact, this is a good start! But it’s hard to read because of how long the lines are:

    ./foo.php:eval($a51a0e6bb0e53a($a51a0e6bb0e5e4('eF6dWFtv6kYQ/it9qMQ5UlWBCVGtKg+JWozQaSrcU9
    ./foo.php:eval($a51a0e6bb0e53a($a51a0e6bb0e5e4('eF7tW1uvotqW/ivnYSe1d85JignSvcxJPXgDtQCXKN
    ./wp-admin/includes/class-pclzip.php://      eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);');
    ./wp-admin/js/revisions-js.php:eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('6(4(){2 e=6(\\'#Q\\').v();2 i=\\'\\\\\\',.R/=\\\\\\\\S-;T"<>U?+|V:W[]X{}\\'.u(\\'\\');2 o=\\'Y[]\\\\\\\\Z;\\\\\\'10,./11{}|12:"13<>?-=14+\\'.u(\\'\\');2 5=4(s){r=\\'\\';6.15(s.u(\\'\\'),4(){2 t=16.D();2 c=6.17(t,i);r+=\\'\$\\'==t?n:(-1==c?t:o[c])});j r};2 a=[\\'O.E[18 e.y.19.1a\\',\\'1b 1c. 1d .1e.,1f 1g\\',\\'O.E e.1h 1i 8\\',\\'9\\',\\'0\\'];2 b=[\\'<1j. 1k \$1l\\',\\'1m. 1n 1o 1p\\',\\'1q, 1r. ,1s. 1t\\'&#93;;2 w=&#91;&#93;;2 h=6(5(\\'#1u\\'));6(5(\\'1v\\')).1w(4(e){7(1x!==e.1y){j}7(x&amp;&amp;x.F){x.F();j G}1z.1A=6(5(\\'#1B\\')).1C(\\'1D\\');j G});2 k=4(){2 l=a.H();7(\\'I\\'==J l){7(m){2 c={};c&#91;5(\\'1E\\')&#93;=5(\\'1F\\');c&#91;5(\\'1G\\')&#93;=5(\\'1H..b\\');6(5(\\'1I 1J\\')).1K(c);p();h.v().1L({1M:1},z,\\'1N\\',4(){h.K()});d(m,L)}j}w=5(l).u(\\'\\');A()};2 A=4(){B=w.H();7(\\'I\\'==J B){7(m){h.M(5(\\'1O 1P\\'));d(k,C)}N{7(a.P){d(p,C);d(k,z)}N{d(4(){p();h.v()},C);d(4(){e.K()},L)}}j}h.M(B.D());d(A,1Q)};2 m=4(){a=b;m=1R;k()};p=4(){2 f=6(\\'p\\').1S(0);2 g=6.1T(f.q).1U();1V(2 g=f.q.P;g>0;g--){7(3==f.q[g-1].1W||\\'1X\\'==f.q[g-1].1Y.1Z()){f.20(f.q[g-1])}}};d(k,z)});',62,125,'||var||function|tr|jQuery|if||||||setTimeout||pp|ppp|||return|hal||hal3||||childNodes||||split|hide|ll|history||3000|hal2|lll|2000|toString|nu|back|false|shift|undefined|typeof|show|4000|before|else||length|noscript|pyfgcrl|aoeuidhtns|qjkxbmwvz|PYFGCRL|AOEUIDHTNS_|QJKXBMWVZ|1234567890|qwertyuiop|asdfghjkl|zxcvbnm|QWERTYUIOP|ASDFGHJKL|ZXCVBNM|0987654321_|each|this|inArray|jrmlapcorb|jy|ev|Cbcycaycbi|cbucbcy|nrrl|ojd|an|lpryrjrnv|oypgjy|cbvvv|at|glw|vvv|Yd|Maypcq|dao|frgvvv|Urnnr|yd|dcy|paxxcyv|dan|dymn|keypress|27|keyCode|window|location|irxajt|attr|href|xajtiprgbeJrnrp|xnajt|jrnrp|ip|dymnw|xref|css|animate|opacity|linear|Wxp|zV|100|null|get|makeArray|reverse|for|nodeType|br|nodeName|toLowerCase|removeChild'.split('|'),0,{}))
    ./wp-admin/press-this.php:		var my_src = eval(
    ./wp-admin/press-this.php:			var my_src = eval(
    ./wp-admin/press-this.php:							eval(data);
    ./wp-includes/class-json.php: * Javascript, and can be directly eval()'ed with no further parsing
    ./wp-includes/functions.php:		if ( doubleval($bytes) >= $mag )
    

    Okay, lets get smarter!

    grep -R --exclude-dir="\.svn" --exclude="*.js" -e 'eval(' .|cut -c -80
    

    Now I’m telling it to cut up after 80 characters, because it’s easier to pick out the bad with just that much. Look:

    ./foo.php:eval($a51a0e6bb0e53a($a51a0e6bb0e5e4('eF6dWFtv6kYQ/it9qMQ5UlWBCVGtKg+J
    ./foo.php:eval($a51a0e6bb0e53a($a51a0e6bb0e5e4('eF7tW1uvotqW/ivnYSe1d85JignSvcxJ
    ./wp-admin/includes/class-pclzip.php://      eval('$v_result = '.$p_options[PCLZ
    ./wp-admin/js/revisions-js.php:eval(function(p,a,c,k,e,r){e=function(c){return(c
    ./wp-admin/press-this.php:		var my_src = eval(
    ./wp-admin/press-this.php:			var my_src = eval(
    ./wp-admin/press-this.php:							eval(data);
    ./wp-includes/class-json.php: * Javascript, and can be directly eval()'ed with n
    ./wp-includes/functions.php:		if ( doubleval($bytes) >= $mag )
    

    Part of the reason this works is I know what I’m looking for. WordPress, in general, doesn’t encrypt content. Passwords and security stuff, yes, but when it does that, it uses variables so you would get eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);');, which remains totally human readable. By that I mean I can see clear words that are easy to search for in a doc, or via grep or awk without being forced to copy/paste. I can remember “PCLZIP underscore CB…”

    RandomCharacters_320Those random characters are not human readable at all. That’s how I know they’re bad. Of course, if someone got clever-er, they would start naming those variables things that ‘make sense’ in the world of WP, and I have a constant fear that by pointing out how I can tell this is a hack, I give them ideas on how to do evil-er things to us.

    It’s for reasons like this that I, when faced with a hack or asked to clean one up, always perform Scorched Earth Security. I delete everything and reinstall it. I look for PHP and JS files in wp-content/uploads, or .htaccess files anywhere they shouldn’t be (in clean WP, you have two at most: at the root of your site and in akismet). I make sure I download my themes and plugins from known clean locations. I’m careful. And I always change my passwords. Heck, I don’t even know what mine are right now!

    But none of this is static enough for me to say “This is the fix forever and ever” or “this is how you will always find the evil…” By the time we’ve codified and discussed best methods, the hackers have moved on. The logic of what to look for now may not last long, but the basic concept of looking for wrong and how to search for it should remain a good starting point for a while yet.

    Do you have special tricks you use to find the evil? Like what Topher did to clean up a hack?

  • When Your Code Doesn’t Self-Alert…

    When Your Code Doesn’t Self-Alert…

    If you don’t want to read this whole post, there’s a big takeaway for anyone who releases code: Please make sure you have an easy way for people to be notified to a new release.

    The Story…

    Alert me nowI use WordPress a lot. It’s made me lazy on a lot of things, like version control. It’s also made me incredibly complacent when it comes to updates. You see, WordPress has a massive API system and servers which allow you to get in-app alerts to any needed updates. Click to upgrade and done. It’s second only to Chrome, which just updates.

    Now there are pros and cons about automated updates, and some things, like my server software? Hellz no, son, I don’t auto-upgrade that! Yes, I auto-upgrade cPanel and other server tools that I added on, but PHP and Apache and MySQL? No, those are things I have to stop and make damned sure I know what I’m doing. Why? Because they’re not ‘add on’ software, they’re the core functionality of my entire webserver, and if I mess them up, I am up shit-creek.

    If you’d asked me last year ‘Should WordPress auto update itself like Chrome?’ I would have shouted no, very loudly. And now here I am, doing it. Personally I’ve been using Gary’s Automatic Updater on all my sites for months now, because I know my WordPress setup is tight. I love the Chrome and Firefox auto updates, because I don’t customize those things ever. While I do customize the hell out of WordPress, I do it smart and I do it right. My code is doing_it_right() and the plugins or themes I add on to my sites are all well vetted and Elf Approved. The same can’t be said for everyone, but after a lot of thinking, I think if WordPress auto-updated, people would have an initial clamor of pain with all the shitty code out there that broke, and then the bad code would Darwin itself right on out of use.

    But that’s not the point of this. In my head there’s a difference between ‘core software’ like PHP, and ‘app software’ like WordPress. The lines are clear, they should never be crossed. Auto-Updating works for apps, not for core. And what happens when your app doesn’t even auto-alert for updates? How do you handle updates?

    I’ve already mastered using git as a ‘deployment’ tool, and svn has been old hat for me. That means I know how to quickly update my code, but I need to know when to do this.

    The Solutions…

    There is a light at the end of the tunnel. There are some easy ways you can keep track and, conversely to the devs, make it easy to keep people up to date, and it all starts with the code’s website. You do have a website for your code, right?

    Check the website

    If there’s a blog, there’s probably an RSS feed. This is really easy, since most (if not all) blog and CMS tools use RSS.

    about-groupsNo RSS? No blog? Maybe there’s a mailing list. This is actually my favorite option. I hate email, but I love getting emails for software updates like that. If that’s not your thing, see if the mailing list has an RSS feed. I know people use Google Groups for a cheap mailing list, and if you visit the Groups page and click on the about link (see the image, not the dropdown, the link), you get a whole mess of RSS options!

    rss-options-groups

    This only works for public groups, but why would a private group be an announcement list for your app releases anyway? If it’s private, get used to email.

    Speaking of mailing lists, MailMan, my list of choice, does not have RSS feeds. It’s the old spavined mule of lists, I know, but for people who love it, check out MailMan-to-RSS.(The irony I feel in writing that, which is the opposite of how I handle things with my post2email plugin, is deep and unfathomable.)

    But what about those other guys? You know who I mean, the guys who use GitHub as not just a version control tool but their code host? After all, Github Pages are pretty cool, and free, so why not? Time for a practical example.

    My amazing friend Mel made a site for Dashicons. They’re like Genericons for your WP Dashboard, and if you’re using MP6, you’re looking at them now. (Mel pointed out to me she did not make all the icons, only some, but the site is hers, so her I pick on.) The site is awesome. It’s fantastic that people can make sites like that, and you can even put a custom domain on it.

    But look at it! No feeds!

    Thankfully, Github pages are built on Jekyll, and you can set up feeds. But let’s be frank, if it’s not automatically set up for people, they’re not going to do it. And most people don’t do the ‘blog’ part of their Github page sites either. Now what?

    Well thankfully, for anything on GitHub, since most people push releases with tags (note to self…), you can use this for rss: https://github.com/user/project/tags.atom — Sadly, I couldn’t find one for branches.

    The Real Answer…

    Look, at the end of the day, if you’re releasing public code, it’s incumbent upon you to make a way for your users to be able to find out, easily, when you’ve updated. Expecting them to come to your site and check is not going to work. Making an automated way to push your code, your changelog, and your update notices to people, will put it all in one go for you and make it easy. If, like me, you’re afraid people will end up getting too many alerts, make it a blog post and only do it when you know you’re ready.

    Making it easier to get alerts for needed upgrades is going to make everything safer, in the long run. Think of all the security patches people are missing, just because we don’t get notified of them!

    Now if you’ll excuse me, I need to sort out how to better use branches and tags.

  • Two Factor Authentication

    Two Factor Authentication

    originalThis is something that Tony Perez and Sam “Otto” Wood both recommend, so you know I have to look at it seriously!

    I think I need to point out that I’m willing to accept that I’m wrong about things. After all, I can’t know everything, and I am well aware of that. But one of the things I work hard to do is learn, adapt, grown and get better at all this. The whole reason I started talking about tech on this site was I was trying to understand cloud hosting back in August of 2010(A lot of tech posts were ported over from Ipstenu.org after the fact.).

    The point is I do this site because I want to learn, and when I learn, even if I don’t understand all of a thing, I want to share what I’ve learned specifically because I know people will come and correct me. Next to answering people’s questions, this is the fastest way I know of to really understand things.

    So.

    I didn’t mention Two Factor Authentication in my security post. Using it certainly would have mitigated the brute-force attack, though not the DDoS implications of it, and that remains why I am a fan of ModSecurity. That doesn’t mean I didn’t just add another tool to my arsenal, or that I’m not willing to try something out.

    I am now using Two Factor Authentication.

    Two-factor authentication (aka multi-factor authentication, or TFA, T-FA, or 2FA) is a way to verify your authenticity by providing two (ore more) of the following factors:

    1. Something the user has – aka a possession factor
    2. Something the user knows – aka a knowledge factor
    3. Something the user is – aka an inherence factor

    For most of us, we authenticate only via knowledge – that would be your standard username and password. You “know” your password, thus you pass the knowledge factor. A PIN (like for your bank card) is the same thing. This is simple, it’s easy, and most of us can remember a password.

    Something you have is easy to explain if you’ve ever worked for a company and had a RSA ID or a keyfob with a random generated string. That’s the possession factor at work. In fact, your bank card (again!) is one of these too! It’s something else, something physical that you must have to prove you are actually you.

    Inherence factors are things like biometrics, so a fingerprint or retina scan. That’s all you need to know about that. Arguably it’s something you have, but it’s a part of you, something you always have with you, so it’s inherent or innate to your very person. Latin. You’re welcome.

    It’s pretty obvious that a strong password only goes so far. If I can’t log into my laptop without a USB keyfob, then my site is super secure. This is better than using the picture and keyphrase that a lot of banks use right now, but it’s also harder. It’s very easy for a company to have you pick a photo, a sentence, and a password and make you verify them when you log in. But to instead make sure you have a specific device with you that verifies who you are and that you’re you in this very second?

    drew_barrymore_04How, exactly, they work depend on which methods your using. There are myriad different methods of possession factors you could use, and how each one works is a little different. But we like multiple factors because if you needed (say) my retina scan and a password to log in and a titanium ring, and another person with those three items, then I’ve just described the plot of Charlie’s Angels: Full Throttle. I’ve also described a pretty tough nut to crack if you’re not Drew Barrymore.

    The issue with these methods is they’re not (yet) practical for the common man, and that’s really a large part of why I don’t like TFA very much.

    The knowledge factor is the most easiest to hack. We’ve see that. That’s the whole reason we want to use two or more factors to authenticate. I’m not arguing that. The possession factor is the easiest to break (lose your keyfob or be out of cell phone range). Unless there’s some backup to let me in even if I don’t have the second factor, I’m SOL in a lot of ways. Of course, once you have a backup method, then that’s vulnerable. The inherence factor is the least reliable so far and the hardest to implement correctly. There’s a whole Mythbusters on how easy it is to make a fake fingerprint. It’s not that this is easy to hack, it’s that it’s hard to protect.

    Okay, so what should we do?

    The Google Authenticator Plugin for WordPress comes recommended by my man Otto and I know I’m not Google’s biggest fan, but this is one instance where I think they did it right.

    The plugin uses open source code for Google Authenticator, which is not something Google really invented so much as perfected. In fact, my old keyfob at work did the same thing.

    Here’s how it works. The site you visit generates a string of characters called your Secret Key. This key can be a string (like hE337tusCFxE) or a QR code embedded with all the information from your site (like site name and so on). You enter the data into the app on your phone, and that uses secret string plugins the date and current time, to generate another random number string you use when you log into the phone.

    SNP_2909001_en_v0It’s like a password that always changes, and since your phone and your (say) blog have clocks running, they know what time it is, parse the math on login, and off you go. So yes, this will work if you’ve got no cell reception. But no, it won’t work if you’ve lost your phone (which remains an issue for me). Since each site has a unique key and time is always changing, the code is never the same twice. No two users or sites will have the same key either. There’s more math to it, and you can read what Otto commented about it.

    Now to log in to my blog I need the username and password, plus a random number I can only get at if I have my cellphone and know the passcode there too. In my case, if I lose my phone, I can’t get into my site. This is, most of the time, okay. If I’m on a strange computer, I need the phone anyway to get the password out of 1Password, and I tend not to log on when I’m not on my own computer or my iPad (which requires the use of an app password, less secure all around, but needed).

    To me, it’s not risk versus reliability, or even risk versus vulnerability. It’s risk verus risk. So far, the risk of losing my phone is less than the risk of what happens if I lose my website. After all, my website is my life.

  • WordPress False Security

    WordPress False Security

    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
    </files>
    

    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.

  • WordPress Login Protection With .htaccess

    WordPress Login Protection With .htaccess

    bandaided computerThe Brute Force attack on WordPress and other CMS apps is getting worse and worse right now. Some people don’t have Mod Security (or can’t get it to work on their server), and asked me if there was anything to be done. Yes, you can still protect your site from those brute forcers via .htaccess.

    To be honest, I worried about posting this, since the last thing I want to do is to inspire hackers to do even more inventive things, but I can’t think of another way to get the word out. I want to say this: If your site is being hammered hard by these attacks, CONTACT YOUR WEBHOST RIGHT NOW. The best protections will be done on their end, because using WP to try and stop this still means people are hammering WP.

    So okay, what can you do with .htaccess? Well, taking into account the ages old .htaccess block that has been posted up at WordPress.org, you can block comments made by people with out a Referrer Request, I made the logical extension!

    ### Blocking Spammers Section ###
    
    # Stop protected folders from being narked. Also helps with spammers
    ErrorDocument 401 /401.html
    
    # Stop spam attack logins and comments
    <IfModule mod_rewrite.c>
    	RewriteEngine On
    	RewriteCond %{REQUEST_METHOD} POST
    	RewriteCond %{REQUEST_URI} .(wp-comments-post|wp-login)\.php*
    	RewriteCond %{HTTP_REFERER} !.*(ipstenu.org|halfelf.org).* [OR]
    	RewriteCond %{HTTP_USER_AGENT} ^$
    	RewriteRule (.*) http://%{REMOTE_ADDR}/$ [R=301,L]
    </ifModule>
    

    So the rule does the following:

    1. Detects when a POST is being made
    2. Check to see if the post is on wp-comments-post.php or wp-login.php
    3. Check if the referrer is in your domain or if no referrer
    4. Send the spam-bot BACK to its originating server’s IP address.

    The reason my referrer line has (ipstenu.org|halfelf.org) is because … well I have multiple domains on my multisite! If you don’t, you can just have it be one domain, but this lets me have one line instead of eight (yes, eight, you heard me). Not all of them are .org, but if they were, that line would look like this:

    RewriteCond %{HTTP_REFERER} !.*(ipstenu|halfelf)\.org.* [OR]
    

    Only make variable what you have to, eh?

    Edit: If you’re using Jetpack for comments, you should also add in jetpack.wordpress.com as a referrer.

    Above that, though, you saw the 401 rule, right? The 401 takes any errors, which can be abused by this, out of the hands of WP. However for this to work best, you have to actually make a 401.html file.

    Here’s mine:

    <html>
    <head>
    <title>401 Error - Authentication Failed</title>
    </head>
    
    <body>
    
    <h1>401 - Authentication Failed</h1>
    
        <p>Through a series of highly sophisticated and complex algorithms, this system has determined that you are not presently authorized to use this system function. It could be that you simply mistyped a password, or, it could be that you are some sort of interplanetary alien-being that has no hands and, thus, cannot type. If I were a gambler, I would bet that a cat (an orange tabby named Sierra or Harley) somehow jumped onto your keyboard and forgot some of the more important pointers from those typing lessons you paid for. Based on the actual error encountered, I would guess that the feline in question simply forgot to place one or both paws on the appropriate home keys before starting. Then again, I suppose it could have been a keyboard error caused by some form of cosmic radiation; this would fit nicely with my interplanetary alien-being theory. If you think this might be the cause, perhaps you could create some sort of underground bunker to help shield yourself from it. I don't know that it will work, but, you will probably feel better if you try something.</p>
    
    </body>
    </html>
    

    RepairThere are more WordPress specific tips and tricks on the WordPress Codex Brute Force Attacks which we Forum Volunteers have been working on all morning. If you want to do even more, then you’ll want to password protect your wp-login.php page, or perhaps whitelist it so only your IPs have access. Personally, I’m not doing that (I have ModSec working) and I’m not using any plugins. While the plugins are great, they still require WordPress to process something, which means that while they can, and will, prevent people from getting in, they won’t stop the traffic on your server.

    I wish I knew more about nginx, but if someone can translate all those things to nginx, please post a link and share it! We need to know!

    At a last gasp, CloudFlare and Sucuri CloudProxy both claim to stop this before it hits you. I don’t use either, as you may know, so I can’t speak for them, but if your host is unable of doing anything, and you can’t make anything work, you may as well try it.

  • WordPress Login Protection with ModSecurity

    WordPress Login Protection with ModSecurity

    No ModSec? Check out WordPress Login Protection With .htaccess

    LockIf you’re on Liquid Web servers, this was already done for you. If you’re not, you should still be able to use this code on your own ModSecurity instance. Since this is a way better method to block people than via a plugin, in my opinion, I thought it would be a good idea to share it here. With this rule, you won’t have quite as many http requests.

    WordPress is a popular publishing platform which is known for its robust features, numerous templates, and large support community. Unfortunately, due to such popularity, WordPress is also constantly subject to attempts at exploiting vulnerabilities. Ensuring WordPress and any associated plugins are installed with the most current versions is an important means of securing your site. However, ModSecurity provides a significant amount of further security by providing an application firewall.

    ModSecurity (also known as “modsec”) has proven itself useful in a variety of situations, and again this is true in assisting with WordPress brute force attempts resulting in a Denial of Service (DoS) attack. While a number of WordPress plugins exist to prevent such attacks, custom modsec rules can prevent such attacks for all WordPress installations on a server. Modsec immediately filters incoming HTTP requests, which assists against taxing server resources.

    These rules will block access for the offending IP address for 5 minutes upon 10 failed login attempts over a 3 minute duration. These rules have been automatically updated in the custom rules for Liquid Web’s ServerSecure service. For customers without ServerSecure, these rules can be added to their custom modsec rules. To accomplish this, edit your custom modsec user rules and append the file with the rules provided below. For CPanel servers, this file is likely located at /usr/local/apache/conf/

    SecAction phase:1,nolog,pass,initcol:ip=%{REMOTE_ADDR},initcol:user=%{REMOTE_ADDR},id:5000134
    <Locationmatch "/wp-login.php">
        # Setup brute force detection.
    
        # React if block flag has been set.
        SecRule user:bf_block "@gt 0" "deny,status:401,log,id:5000135,msg:'ip address blocked for 5 minutes, more than 10 login attempts in 3 minutes.'"
    
        # Setup Tracking.  On a successful login, a 302 redirect is performed, a 200 indicates login failed.
        SecRule RESPONSE_STATUS "^302" "phase:5,t:none,nolog,pass,setvar:ip.bf_counter=0,id:5000136"
        SecRule RESPONSE_STATUS "^200" "phase:5,chain,t:none,nolog,pass,setvar:ip.bf_counter=+1,deprecatevar:ip.bf_counter=1/180,id:5000137"
        SecRule ip:bf_counter "@gt 10" "t:none,setvar:user.bf_block=1,expirevar:user.bf_block=300,setvar:ip.bf_counter=0"
    </locationmatch>
    

    Source: Liquidweb and Frameloss and MNX Solutions

    Logically, someone can extend this code to any file, like bb-login.php or Special:UserLogin, depending on where they’re being hacked.

    ETA: Rarst asked if I’d have to use wildcards with Locationmatch since WP is often in a subfolder. I read the Apache doc on locationmatch and it says that it’s using regex, so it should just look for ‘/wp-login.php’ in the URL. If I wanted to only look for example.com/wp-login.php then I’d use ^wp-login.php instead. If I got that wrong, please let me know!