Half-Elf on Tech

Thoughts From a Professional Lesbian

Category: How To

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

  • Trading APC for Zend

    Trading APC for Zend

    zend-optimizerThe last thing I did before Passover was a totally unannounced, not telling anyone, surprise flip from my old standby, APC, to the new hotness, Zend.

    As of PHP 5.5, Zend Optimizer+ will be included. Back in 2009, I decided to use APC for a couple reasons: It was made by the PHP blokes and it worked well with mod_php and MediaWiki. Since PHP decided to change, I joined in. Why the change? Zend went open source.

    Installing Zend

    I grabbed the latest tagged version from the official GitHub repository, since I don’t cotton to using bleeding edge all the time.

    $ wget https://github.com/zend-dev/ZendOptimizerPlus/archive/v7.0.1.zip
    $ unzip v7.0.1
    $ cd ZendOptimizerPlus-7.0.1/
    $ phpize
    $ ./configure
    $ make
    $ make install
    

    That gave me the final output of: Installing shared extensions: /usr/local/lib/php/extensions/no-debug-non-zts-20100525/ You want to hang on to that path, because we’re going to edit php.ini and put this in below the line for IonCube, if you have that installed:

     zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20100525/opcache.so
    

    Replace the path with your own.

    I also followed their advice on the recommended config and put this in my php.ini as well:

    [Zend]
    opcache.memory_consumption=128
    opcache.interned_strings_buffer=8
    opcache.max_accelerated_files=4000
    opcache.revalidate_freq=60
    opcache.fast_shutdown=1
    opcache.enable_cli=1
    

    Since I had APC there before, I removed all those lines while I was at it. Restarted PHP (well, httpd in my case, I like a clean slate) and I was done.

    Cleanup

    As I mentioned before, I removed the APC lines while I was in the php.ini and, since I installed it via a downloaded pecl, I just removed the apc.so file from the same folder where opcache.so was installed.

    On WordPress, I also removed the APC Object Cache Backend plugin from the two sites that had it, since I’m not using APC. There doesn’t appear to be an equivilent for Zend, but I may or may not need that.

    For MediaWiki, I had to edit LocalSettings.php and remove $wgMainCacheType = CACHE_ACCEL; as that’s no longer being used. Then I went into my maintenance folder and ran php rebuildLocalisationCache.php --force to flush everything and my errors went away. MediaWiki has a pretty different cache method than other apps, and for yonks flat out didn’t work with Zend. It does now, and there seemed to be no speed loss without an OpCode cache.

    None of my other apps seemed to care, so I moved on to see how this all played out.

    Results

    Much like the ManageWP guys, I saw an immediate drop in memory and CPU. And I’m still on PHP 5.4! There was an initial spike while I was mucking around, which I expected, and then everything dropped. Some things did not so much drop as level out and change my ‘spike’ range. Normally my load average for 1 minute would spike at 0.8 or so every hour. Now it’s spiking at 0.5, give or take, which is a success in my book.

    Load Avg 5min - Post Zend
    Can you guess when Zend went on?

    Before someone points out that, hey, this is hardly a real check, I want to note that I was having one of those 200% traffic days. Actually I was having a few of them in a row, and I know it’s going to go up from there. Here’s a quick look at my traffic:

    traffic ga-traffic

    The real test will be tonight and tomorrow, though, as I push some large data (videos) on a popular topic, but frankly, this is looking good.

    The benchmarks used in the ticket to include Zend in PHP (why are they using WP 2? I have no idea) were startling compared to normal PHP. Compared to APC it’s a minimal kick in the pants, but the assurance of bundling to come speaks for itself. This is where PHP is going.

    APC v Zend Benchmarks
    Credit: Dimitry

    I’m not yet on PHP 5.5, but I’ve already found that 5.4 is a notable kick to my speed. Zend took me from a B to an A in PageSpeed and YSlow (when you tell YSlow that I’m a small blog and not Amazon). Also it ‘feels’ faster, which is totally subjective, but still a valid remark. The site feels fast, it doesn’t hang (yet) and it didn’t crash over Passover!

    Former bbPress regular, _ck_, went and wrote a neat little Control Panel for Zend Optimizer+ which I snagged and tossed into my secret bin for poking at later.

    Since I’m the Zend Rookie, anyone have any tips and tricks for configuring it to make it sing? Remember, I don’t just use WordPress on this box!

  • Genericon’d Menus

    Genericon’d Menus

    Screen Shot 2013-03-31 at  31 Mar - 1.35.26 AMQuick and dirty, I wanted a menu to have Genericons. Menus are crazy extendible and I use them in sidebars all the time to make a list of links formatted in a nice way. So why not a Genericon? After all, I know menus are really glorified Custom Post Types.

    Don’t they look pretty? It’s stupid easy.

    Screen Shot 2013-03-31 at  31 Mar - 1.38.34 AM

    URL: http://facebook.com/you
    Navigation Label:
    Title Attribute: Facebook
    CSS Classes (optional): social-buttons

    Then all I had to do is fiddle with CSS. li.social-buttons .genericons is applicable here, and that’s why I gave it the CSS class. If I wanted to have it be a genericon plus text, then the Navigation Label would have been Facebook instead. If you don’t want the genericon to be part of the link, make the label just the link name, and add genericon genericon-facebook to the CSS classes.

  • Goodbye Google Reader

    Goodbye Google Reader

    Ain't no one fucks with tiny hippo
    Credit: Poorly Drawn Lines
    You know, I get it. RSS is not a popular tool for people who like the ‘river’ flow of data. If you like everything to flow into your stream and back out, like Tumblr or Twitter or Facebook, then the loss of Google Reader is meaningless to them. “Why do I want another inbox?” they argue. That’s all fine and dandy for you, but we have to accept that different people process data differently. Some people like to watch news come in live, like reading a CNN ticker, and if they miss it that’s okay. Others of us like to say ‘These are the things I like, save a note when they happen and I’ll read them when I can.’ They’re two different workflows, and they appeal to different people.

    Me? I’ve been using Tiny Tiny RSS for just under a year now, and I’ve actually figured out how to do everything I want, with key-commands. Since I use multiple devices for my news consumption (two laptops, an iPad, etc etc) having this web-based was a real killer. And while I could use a cloud device, I’ve never found one that worked across Windows and Macintosh, and wasn’t blocked by The Bank. That’s less of an issue now, but having it all on my own server beings me back to my oldest bugaboo ever: Owning my data.

    If there’s anything you get from the whole Google Reader fiasco, it should be this: Google gave, and Google has taken away. Everyone who is mad that Google “Broke their trust.” just hasn’t been paying attention to the last year or two at Google. Google Apps for Email anyone? It’s not free anymore. But let’s not belabor the I-told-you-so part and get to the meat of the post.

    Tiny Tiny RSS

    So installing this is really easy for anyone who’s installed any PHP/SQL app before, I’m not going to get into that, you can read the Install Notes yourself. What I will point out are the plugins I find most useful, and the quirks to keep in mind.

    First of all, ttrss is more like MediaWiki than WordPress. This means the upgrade is mostly manual for some of us, and you activate plugins by editing the config.php file. However. There is also a plugin interface in settings, so the define’d plugins are basically like Network Activated, which is great if you have multiple users. The other plugins are in the preferences.

    Plugins work like this:

    define('PLUGINS', 'auth_internal, digest, updater');
    

    And I am fond of the following:

    • auth_internal – Authenticates against internal tt-rss database
    • digest – Digest mode for tt-rss (tablet friendly UI) Turn this on if you use your iPad
    • updater – Updates tt-rss installation to latest version.

    Interestingly, I cannot run the web-updater from my server, and it’s certainly to do with my PHP settings. That said, the manual upgrade is like WP: upload files, refresh DB, drink beer. I don’t mind it at all. There are the other available plugins under Preferences -> Plugins, and they make a lot of sense just by looking at them. Obviously they’re easy to see based on what you’d want to use. There’s no Twitter Plugin since Twitter’s new API made it a hassle to tweet and I don’t blame them on this front.(Tangental: Speaking of asinine moves, Twitter’s new API may require us to use it to embed tweets. The answer to the direct question was predictably vague.)

    But if you’re here today, you probably want a more Google Reader type experience. I would enable ‘Combined Feed Display’ under preferences and disable ‘Automatically expand articles in combined mode’. This will bring the ability to expand posts. It doesn’t collapse them quite right or at all via mouse, HOWEVER everything you want can be done via key commands.

    • s – Mark an article as starred.
    • n (or down-arrow) – go to the next article
    • p (or up-arrow) – go to the next article
    • u – toggle read/unread

    That’s pretty much all I needed, and once I read them, they were blindingly obvious. You can see them when you’re

    You can style CSS to fiddle with the layout, but so far I’ve not figured out how to make it display the title of the feed.

    greader feeds

    versus

    Screen Shot 2013-03-14 at 11.54.09 AM

    On the other hand, I know the favicons of most of these sites so with a little CSS jiggering I was able to make it look a little better for myself. Here’s my CSS:

    div.postReply div.postContent, body#ttrssMain, body#ttrssPrefs, body#ttrssLogin, body,blockquote,#content-insert blockquote, #headlines-frame blockquote, .dijitContentPane blockquote  { font-size:14px;}
    div.postReply div.postHeader { font-weight:bold;font-size:14px;}
    .hlScorePic {display:none;}
    img.tinyFeedIcon {float:left;}
    .Unread span.titleWrap  { font-weight:bold; }
    

    From there on out, you can play with design as you like it. It’s clean, it’s simple, and best of all, it’s Open Source so if you like most of it, you can fork the rest!

  • IE 8 and SVG Smilies

    IE 8 and SVG Smilies

    I don’t like the default smilies in WP. There, I said it. They’re old and busted, so I use the smilies_src to replace them with nicer ones. Recently it came to my attention how old and busted my cute PNGs looked on my iPad and any retina capable computer, so I fiddled around and decided SVG graphics were the way to go. They scale well, and they work on all modern browsers, yay!

    Oh, wait, IE 8 is not a modern browser and it’s pretty common out there… In fact my old job still uses it on a lot of PCs. And so does at least one user on this site (actually 5, and one more uses IE 6, for crying out loud!) so I came up with this:

    // Move Smilies
    add_filter('smilies_src','my_smilies_src', 1, 10);
    function my_smilies_src($img_src, $img, $siteurl) {
        $img = rtrim($img, "gif"); // Remove GIF
    
        if ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE 8' ) || strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE 7' ) ) {
            $type='png';
        }
        else {
            $type='svg';
        }
        return 'http://domain.com/images/smilies/'.$img.$type.'';
    }
    

    That said, I wish we had more modern smilies available for WP. Finding a set that look okay (like the ones I have here) that are also retina capable are not easy. I could use user agents to go the other way, checking if the visitor was on a ‘new’ iPad or iPhone and show them retina that way, but to the best of my knowledge, there’s no way (yet) to do it so that a retina MacBook also gets the nicer view. With that in mind, I went just with SVG, which scale naturally and meet all of my needs.

    By the way, thank Otto for the smilies filter. You can use it for normal filtering too:

    add_filter('smilies_src','ipstenu_smilies_src', 1, 10);
    function ipstenu_smilies_src($img_src, $img, $siteurl){
        $img = rtrim($img, "gif");
        return $siteurl.'/images/smilies/'.$img.'png';
    }
    

    That’s what I use here.