Half-Elf on Tech

Thoughts From a Professional Lesbian

Author: Ipstenu (Mika Epstein)

  • 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!

  • Customizing Author’s Comments on Genesis

    Customizing Author’s Comments on Genesis

    commentsI like Genesis, but I wanted to make some tweaks to how comments looked.

    1) Bigger Avatar Size
    2) ‘Mark’ post authors and site admins

    This was pretty easy, since they have a filter in for comments, so all I had to do what tell it to replace their avatar size with mine, and then to use my callback. It’s in my callback that I did an extra check. If the commenter is an admin, they’re labled ‘Site Admin’ and if they’re the post author, it’s ‘Post Author.’

    The end result looks like this (I’m commenting on a CSI episode where someone’s hiding in the walls):

    Example Comment

    The only hard part of the code was finding I can’t filter the comment callback, but I have to totally replace it with my own. Bummer, but not insurmountable. StudioPress has a nice document on comment filters which explained how I could override settings like avatar size and callback, which lead me to my next step, the filter:

    // Customize Comments for avatar size and MY callback
    add_filter('genesis_comment_list_args', 'mysite_comment_list_args');
        function mysite_comment_list_args($args) {
            $args['avatar_size'] = '90';
            $args['callback'] = 'mysite_comment_callback';
            return $args;
    }
    

    Once I have the filter, I have to create the mysite_comment_callback. This is something I basically copied from the source of my theme, taking the whole function for genesis_comment_callback and changing what I wanted.

    /** replace comment callback with my own **/
    function mysite_comment_callback( $comment, $args, $depth ) {
    
    	$GLOBALS['comment'] = $comment; 
    	global $post; 
    	?>
    
    	<li <?php comment_class(); ?> id="comment-<?php comment_ID(); ?>">
    
    		<?php do_action( 'genesis_before_comment' ); ?>
    
    		<div class="comment-header">
    			<div class="comment-author vcard">
    				<?php echo get_avatar( $comment, $size = $args&#91;'avatar_size'&#93; ); ?>
    				<?php echo ( user_can( $comment->user_id, 'administrator' ) ) ? '<span class="mysite-title">Site Admin</span>' : (
    						( $comment->user_id === $post->post_author ) ? '<span class="mysite-title">Post author</span>' : '' ) ; ?>
    				
    				<?php printf( '<cite><span class="fn">%1$s</span></cite> <span class="says">%2$s:</span>',
    						get_comment_author_link(), 
    						apply_filters( 'comment_author_says_text', __( 'says', 'genesis' ) ) ); ?>
    		 	</div><!-- end .comment-author -->
    
    			<div class="comment-meta commentmetadata">
    				<a href="<?php echo esc_url( get_comment_link( $comment->comment_ID ) ); ?>"><?php printf( __( '%1$s at %2$s', 'genesis' ), get_comment_date(), get_comment_time() ); ?></a>
    				<?php edit_comment_link( __( '(Edit)', 'genesis' ), '' ); ?>
    			</div><!-- end .comment-meta -->
    		</div>
    
    		<div class="comment-content">
    			<?php if ( $comment->comment_approved == '0' ) : ?>
    				<p class="alert"><?php echo apply_filters( 'genesis_comment_awaiting_moderation', __( 'Your comment is awaiting moderation.', 'genesis' ) ); ?></p>
    			<?php endif; ?>
    
    			<?php comment_text(); ?>
    		</div><!-- end .comment-content -->
    
    		<div class="reply">
    			<?php comment_reply_link( array_merge( $args, array( 'depth' => $depth, 'max_depth' => $args['max_depth'] ) ) ); ?>
    		</div>
    
    		<?php do_action( 'genesis_after_comment' );
    
    	/** No ending </li> tag because of comment threading */
    
    }
    

    Finally it was a simple CSS to make it look snazzy.

    .bypostauthor span.mysite-title {
    	padding: 2px 5px;
    	padding: 0.15rem 0.4rem;
    	font-size: 11px;
    	font-size: 0.785714286rem;
    	line-height: 1;
    	font-weight: normal;
    	color: #7c7c7c;
    	background-color: #FFFFCC;
    	background-repeat: repeat-x;
    	background-image: -moz-linear-gradient(top, #ffc, #fc3);
    	background-image: -ms-linear-gradient(top, #ffc, #fc3);
    	background-image: -webkit-linear-gradient(top, #ffc, #fc3);
    	background-image: -o-linear-gradient(top, #ffc, #fc3);
    	background-image: linear-gradient(top, #ffc, #fc3);
    	border: 1px solid #d2d2d2;
    	border-radius: 3px;
    	box-shadow: 0 1px 2px rgba(64, 64, 64, 0.1);
    	float: right;
    }
    

    I should point out that I have no experience with editing comments.

  • Your Photos, Your Way

    Your Photos, Your Way

    PressGramI’m funding PressGram on Kickstarter and you should too.

    I like Open Source. Surprise!

    I don’t mind paying for products (as witnessed by the fact that I have paid for this theme, and even the old DevPress and ThemeHybrid ones I don’t use anymore. I have a slew of plugins I paid for, and all in all, I think every dime was money well spent. Paying for open source makes sense.

    So there’s this guy I know from the Internet, John Saddington, who likes taking photos, and he likes social media, but he wonders, like I often do, what happens when those outlets go away? Where are all my photos if TwitPic or YFrog vanishes? Or if Facebook deletes my account?

    They’re gone.

    John loves WordPress. So do I. John loves photos. Well. I fiddle around with them, but the point is he wants to built something that is way more than ‘just’ a plugin. He wants to make a free iPhone app… look, this is what he wants:

    The premise is simple: I wanted to post filtered photos from my iPhone 5 but without worrying about any privacy or licensing issues (and we’re not interested in asking you to upload photo IDs). In other words, I wanted complete and total creative control of my images and content (as well as the pageviews).

    photo-littleAnd this will post to WordPress, which is so simple, we have a one-click installer at DreamHost for you to use to make it. Imagine that. You could have a photoblog with a couple clicks.

    When I read that John was making PressGram, I had to poke at it, even though it’s not Open Source. It’s an Apple iOS app. I’m not shocked that it’s not open source, and after consideration, I don’t mind. It doesn’t have to be. As long as the plugin is open source (and frankly, given WordPress’s API, I can easily envision how it would be without stepping on closed source apps), it’s good to go.

    John knows his shit. He shares the same concerns and doubts about social media as I do, he rails on Facebook for the same things I do. He’s a guy whose ethics I can get behind. And he’s a guy whose code I can get behind. Remember I review plugins. I’ve seen his code. It’s good.

    So yeah, I’m supporting him so you can have a free app. Go figure. And as with most of the things I kickstart, I get no swag back (I think I get a kudos and a link somewhere), because I like to give for the spirit of giving most of the time. I’ll be getting the Veronica Mars DVD, but I’d be buying that anyway.

    Give in. You know you want this. Pay $5 instead of risking your content belonging to someone else.

  • Chrome Dumps Webkit

    Chrome Dumps Webkit

    Chromium, the machine behind Chrome, has dumped Webkit.

    This speaks for us all:

    bridesmaids

    I’m not sure what’s going viral first, that gif or a ‘Don’t blink’ joke that someone has yet to make up. Oh, the new system is called Blink, and it’s open source.

    We know that the introduction of a new rendering engine can have significant implications for the web. Nevertheless, we believe that having multiple rendering engines—similar to having multiple browsers—will spur innovation and over time improve the health of the entire open web ecosystem.

    Don't BlinkYes, this means we all get to use even more browsers to make sure our sites look okay on all of them. Again. Thanks. And while they say it’s ‘based on’ webkit, that’s about as ‘related to’ as when Law & Order would say ‘The following episode is based on a true story, only names have been changed…’

    This prompted my coworker Shredder to opine “Are Google and Mozilla late-april-fooling us?”

    Did you miss the Mozilla/Samsung partnership? Or maybe you didn’t notice that Opera moved too Webkit in February. No wait! OPERA SWITCHES TO BLINK

    Okay, Rarst is right: They’re out to screw over Apple.

    Well, I don’t know about you, but I can’t wait to see the new ways in which my sites break!