WordPress: large scale brute force attack

A brute force attack started last week and is targeting WordPress installations around the globe. The attack is not only performed by a few hosts but by a network of over 90,000 IP addresses.

A brute force attack is a method to gain login information. In this type of attack, discretion and efficiency plays no role at all. It involves going through a sequential search for login information. Through all these login attempts, the attack also increases the resource usage of the website. Once the login information have been acquired, a backdoor is usually installed and the host is then used as one more point from which brute force attacks can be carried out (the backdoor lets the attackers control the site remotely). Sometimes the backdoor is kept in there for a while without any other noticeable activities waiting for a larger attack to be launched.

In this concrete case, the attackers try to gain access using the default WordPress administrator account “admin” and try thousands of passwords. CloudFlare and HostGator reported a large scale attack. ClouFlare claims to have blocked 60 million requests just in an hour.

If you have a WordPress site, I’d suggest to:

  1. make sure both you Operating System and WordPress installations are up-to-date.
  2. not use “admin” as you administrator user name. Both the username and the password protect your site. If an attacker can easily find the username (because it’s ‘admin’ or ‘root’), then half the job is already done.
  3. make sure the password is strong enough.

Additional security measures can include:

  1. use some a Content Delivery Network with built-in security (like CloudFlare) or a security proxy (like Sucuri).
  2. use Two-Factor Authentication.
  3. limit the IP addresses which can access the login page.

WordPress: Disable comments on old posts

Depending on the topic of you posts, old posts might not be so relevant anymore (e.g. if they are news related). In this case comments added to old posts are most probably spam. So it makes sense to disable comments and ping backs after a certain number of days (e.g. 365 days).

Enabling or disabling comments is done in the database with the comment_status column of wp_posts e.g.:

UPDATE wp_posts SET comment_status = 'closed';

This will disable comments for all comments in the database. Of course if it is what you want to do, you can also from the beginning disable comments for all new posts. But this will not affect existing posts. For existing posts, you need the SQL statement above or you have to select all posts and edit the comment field in the administation UI.

Note that trackbacks and pingbacks are not affected by this. For them you have to update the column ping_status instead of comment_status.

If you want to only enable comments for registered users, the SQL statement would look like this:

UPDATE wp_posts SET comment_status = 'registered_only';

Now, if you do not want to disable comments for all posts but only for old posts, you have two possibilities:

  1. Create a cron job updating the comment_status column of old posts e.g.:
    UPDATE wp_posts SET comment_status = 'closed', ping_status = 'closed' WHERE post_status = 'publish' AND post_date < '2012-02-16';
  2. Introduce a filter which will based on the date make WordPress think comments where disabled in the database

The latter is the option we'll explore here.

We'll have to define a function doing the magic and add it as a filter to the_posts. Like this our function will be called everytime the posts are loaded. Our function will be called with an array of posts. We are only interested in the single post view. So we will basically make sure that we're there and update the comment_status and ping_status attributes of the first post. Here the code:

function close_old_comments( $posts ) {
	//Number of days after which comments should be closed
	$no_days = 365;
	//Only do this for pages where a single post is displayed
	if ( !is_single() ) { return $posts; }
	//Post age in seconds
	$post_age = time()-strtotime( $posts[0]->post_date_gmt);
	//Close comments for posts older than a year i.e. 365 days
	if ($post_age > ($no_days*24*60*60)) {
		//Disable comments
		$posts[0]->comment_status = 'closed';
		//Disable pingbacks and trackbacks
		$posts[0]->ping_status = 'closed';
	//Return the updated post lists
	return $posts;
//Register our function as a filter on the_posts
add_filter( 'the_posts', 'close_old_comments' );

Add the code above to functions.php, save and go to a post older than a year. The new comment form will be gone.

WordPress: Remove addtional line breaks

When displaying a post, WordPress has the bad habbit of adding a few <br> tags. This often messes with the layout especially when you add images on the left or right side.

This is done by the wpautop filter. This function does not only add line breaks but also replaces double line breaks by HTML paragraphs i.e. <p>…</p> tags.

If you want to completely switch it off, you can just remove the filter by adding the following to your functions.php:


If you want to keep the paragraph tags but just make sure that no additional line breaks are inserted, you’ll need to replace the filter by your own. wpautop takes two parameters:

  • $foo: The text to be formatted.
  • $br: A boolean saying whether additional line breaks should be added.

So basically we just need to call the function with the text to be formatted and $br=0:

function wpautop_wo_br($foo){
    return wpautop($foo,$br=0);


By adding this to your functions.php, you’ll get the desired effect. It’s that simple !

WordPress: Display a table with custom fields at the end of a post

If you have defined a few custom fields in your post and want them appended in a tabular form to the post, you have two possibilities:

  1. Add some code in content.php just after the call to the_content() to display the table.
  2. Add a filter for ‘the_content’ in order to append the table to the content.

The difference is basically that:

  • In the second solution, the table will be in the entry-content DIV tag. If you want to have it outside of this div, you need to make the change in content.php.
  • With the first solution, if you have another plugin (e.g. a related post plugin) using the second solution to append something to the content, this will come between the original content and the table.

At first I used the first solution because it was straight forward. But after installing the YARPP plugin, the related posts were displayed between the content of the post and the table. So I switched to the second solution.

For this you need to create a new function in functions.php getting the content as parameter and appending the HTML code for the table. And you need to add a filter for ‘the_content’ using this function.

function add_product_spec_to_content($content) {
	if(get_field('befestigung') || get_field('gurtsystem') || get_field('gruppe') || get_field('typ') || get_field('amazon') || get_field('bezug') || get_field('gewicht')) { $content = $content . '<table id="product-specification" cellspacing="0" border="0" style="border-collapse:collapse;"><tbody>'; }

	if(get_field('befestigung')) { $content = $content . '<tr><td>Befestigung</td><td>' . get_field('befestigung') . '</td></tr>'; }
	if(get_field('gurtsystem')) { $content = $content . '<tr><td>Gurtsystem</td><td>' . get_field('gurtsystem') . '</td></tr>'; }
	if(get_field('bezug')) { $content = $content . '<tr><td>Bezug</td><td>' . get_field('bezug') . '</td></tr>'; }
	if(get_field('gruppe')) { $content = $content . '<tr><td>Gruppe</td><td>' . get_field('gruppe') . '</td></tr>'; }
	if(get_field('typ')) { $content = $content . '<tr><td>Typ</td><td>' . get_field('typ') . '</td></tr>'; }
	if(get_field('gewicht')) { $content = $content . '<tr><td>Gewicht</td><td>' . get_field('gewicht') . ' kg</td></tr>'; }
	if(get_field('amazon')) { $content = $content . '<tr><td>Preis</td><td>' . get_field('amazon') . '</td></tr>'; }

	if(get_field('befestigung') || get_field('gurtsystem') || get_field('gruppe') || get_field('typ') || get_field('amazon') || get_field('bezug') || get_field('gewicht')) { $content = $content . '</tbody></table>'; }

	return $content;

add_filter( 'the_content', 'add_product_spec_to_content', 1150 );

I here use 1150, because I know YARPP uses 1200. And since I want to append my table before YARPP appends the related posts, I need to use a value less than 1200. But the value should be high enough so that the table is displayed after any other appended things.

Here an example of how it looks like (with the YARPP plugin enabled).

WordPress: query posts with multiple custom fields

When you use custom fields to define multiple properties of posts and want to provide filtered views based on a combination of filters, you’ll first come to the function query_posts.

I assume here you know how to properly use query_posts e.g. you know you have to reset the query after using it (with wp_reset_query).

You can define what you want to retrieve in the arguments of the function e.g.:

query_posts( 'p=53' ); //will query the post with ID=53
query_posts( 'cat=6' ); //will query all posts in the category with ID=6

If you want to perform some more complex queries it is generally better to provide a query parameter array as argument than a query string:

$args=array('category_name' => 'my-category-slug', 
			'posts_per_page' => 10, 
			'orderby' => 'date', 
			'order' => 'ASC');

It makes it more readable and allows you to add a meta_query element to also filter according to a custom field:

$args=array('category_name' => 'my-category-slug', 
			'posts_per_page' => 10, 
			'orderby' => 'date', 
			'order' => 'ASC',
			'meta_query' => array(array('key' => 'my_custom_field_key',
										'value' => $my_custom_field_key))

Or even with multiple custom fields:

$args=array('category_name' => 'my-category-slug', 
			'posts_per_page' => 10, 
			'orderby' => 'date', 
			'order' => 'ASC',
			'meta_query' => array(array('key' => 'my_custom_field_key',
										'value' => $my_custom_field_key),
								  array('key' => 'my_custom_field_key2',
										'value' => $my_custom_field_key2))

While implementing the search form on kinderautositz-kaufen.de, I had to do something like this:

If the user chooses the value “value1” in the filter, then display posts with the custom field set to “value1” or “value1 or xxx” or “xxx or value1”.
If the user chooses the value “value2” in the filter, then display posts with the custom field set to “value2” or “value2 or xxx” or “xxx or value2”.

This makes it of course easier to directly work with SQL than to use arrays to define the criteria.

Here’s how I did it:

First you need to setup some filters to add the fields, joins and where clauses to the generate statement.

Add the following to your functions.php file (e.g. at the end of the file):

<?php function my_posts_fields ($fields) {
   global $my_global_fields;
   if ($my_global_fields) $fields .= $my_global_fields;
   return $fields;
function my_posts_join ($join) {
   global $my_global_join;
   if ($my_global_join) $join .= $my_global_join;
   return $join;
function my_posts_where ($where) {
   global $my_global_where;
   if ($my_global_where) $where .= $my_global_where;
   return $where;
function my_posts_orderby ($orderby) {
   global $my_global_orderby;
   if ($my_global_orderby) $orderby = $my_global_orderby;
   return $orderby;

You then need to update the global variables $my_global_fields, $my_global_join, $my_global_where and $my_global_orderby in your page template:

	if(!empty($_REQUEST['my_custom_field_key'])) {
		$mam_global_fields = $mam_global_fields . ', my_custom_field_key.meta_value my_custom_field_value';
		$mam_global_join = $mam_global_join . " JOIN $wpdb->postmeta my_custom_field_key
			ON ({$wpdb->posts}.ID = my_custom_field_key.post_id AND my_custom_field_key.meta_key = 'my_custom_field_key'
			AND (my_custom_field_key.meta_value = '{$_REQUEST['my_custom_field_key']}' OR my_custom_field_key.meta_value LIKE '%%{$_REQUEST['my_custom_field_key']} or %%' OR my_custom_field_key.meta_value LIKE '%% or {$_REQUEST['my_custom_field_key']}%%'))";
	if(!empty($_REQUEST['my_custom_field_key2'])) {
		$mam_global_fields = $mam_global_fields . ', my_custom_field_key2.meta_value my_custom_field_value2';
		$mam_global_join = $mam_global_join . " JOIN $wpdb->postmeta my_custom_field_key2
			ON ({$wpdb->posts}.ID = my_custom_field_key2.post_id AND my_custom_field_key2.meta_key = 'my_custom_field_key2'
			AND (my_custom_field_key2.meta_value = '{$_REQUEST['my_custom_field_key2']}' OR my_custom_field_key2.meta_value LIKE '%%{$_REQUEST['my_custom_field_key2']} or %%' OR my_custom_field_key2.meta_value LIKE '%% or {$_REQUEST['my_custom_field_key2']}%%'))";

You can then display the results like this:

<?php while ( have_posts() ) : the_post(); ?>
    <div class="entry">
        <header class="entry-header">
          <h2 class="entry-title"><?php the_title(); ?></h2>
		<div class="entry-content">
		</div> <!-- entry-content -->
	</div> <!-- entry -->
<?php endwhile; // end of the loop. ?>

WordPress: A URL redirection page template

Sometimes you want an archive page, a search page or a post to be accessible using a shorter or nicer URL.
Let’s say I want my author page to be accessible not only with the URL https://benohead.com/author/hb/ but also with the URL https://benohead.com/about-me/. You can of course just create this about-me page and put in some redirection code. Let’s say you want to do it more often and do not want to always have to remember the code to add to redirect. Then you’d need a special page template used for redirection and be able to configure the redirection URL in the individual pages.

There’s a perfect tool for this: the custom fields. You can just create a template reading a new custom fields (e.g. called “redirect_url”) which contains the URL you want to redirect to and redirecting the visitor to this URL.

You then just need to create a new page, choose this redirection template, set the custom field and publish the page.

Here’s a short description how it should be done.

First you have to create the redirection page template. A page template is very easy to create. Just create a new file in your theme directory e.g. wp-content/themes/mythemename/redirection-page.php. Make sure this new file has the same owners and access rights as the other files in the directory. This file is then visible in the Theme Editor. Open it there and add the following:

 * Template Name: Redirection Template
$redirect_url = get_post_meta($post->ID, 'redirect_url', true);

Here a few explanations about the code:

 * Template Name: Redirection Template

This basically identifies the file as a page template which will be displayed as “Redirection”.

$redirect_url = get_post_meta($post->ID, 'redirect_url', true);

We fetch the custom variable using the get_post_meta function. The third parameter set to true tells WordPress that we do not want to get an array back but a single value.


This performs the redirection to the requested URL.

Another way to do it is:

 * Template Name: Redirection Template
$redirect_url = get_post_meta($post->ID, 'redirect_url', true);
header('Location: '.$redirect_url);

Now you can use this template when creating a new page:

template available

And you need to define the redirection URL in the custom field redirect_url. By default the custom field area is not displayed for pages. You need to enable it in the screen options:

screen options display custom fields

Then you will see the following below the page text:

empty custom fields area

If it is the first time you use this custom field, it will not be in the list and you’ll have to add it by clicking “Enter new”:

enter new redirect custom field

The value you set is the URL you want to redirect to. When done, just publish the page.

The only difference between the two solutions using wp_redirect and header is that with wp_redirect, you will see the redirection URL in the URL field in the browser and with header, you will see the URL of the redirecting page.

Quick Tip: How to Code a Scrolling Navigation Bar

So you’ve seen them on other people’s websites and you want to know how you can have one too? The scrolling navigation menu seen on many websites is really easy to replicate in WordPress. Help your readers out, let them click the menu from any point on the page. Here’s how…

This is basically only about some relatively basic CSS but I guess it might be interesting to time of you.

WordPress: Meta Widget Customizer available on wordpress.org

I’ve just uploaded the plugin I’ve written to customize my meta box (lower widget in the sidebar). The meta widget part of the default WordPress installation provides 5 links but does not allow you to select which of them you want to display. That’s why I’ve started writing this widget, just to be able to select which of those 5 links I want to see.

Then I wanted to be able to display a few additional links as well. Since this widget is constantly evolving and now provides much more functionality than the original WordPress Meta Widget, I’ve decided to host is on wordpress.org in case anybody else is looking for such a widget.

Here the link.

The current functionality are:

  • Show/hide any of the 5 default links.
  • Select a link category. All links in this category will be shown in the widget.
  • Enter a feed URL and define how many items should be shown.
  • Show a password lost link.
  • Show a link to the site admin (this link is also shown when register is active and the user is logged in, but you might need it if you hide register).
  • Show a link to the XHTML validator

Coming up in the next version:

  • Display a google search box to search this site or the web.

The links displayed in the Meta Widget can be configured either in the plugin settings page or in the widget administration.

WordPress: Redesigning the login dialog

Here’s how the login dialog of my blog looked like a few days ago:
WordPress default login dialog

It’s the standard login dialog and well looks very standard… I thought that since I moved from Blogger to WordPress over 6 months ago it was time to also get a login page fits to the blog UI style and looks more personalized than that.

You cannot directly influence the login dialog by adding a few CSS instructions in the stylesheet of your theme. But you can work in the theme functions (functions.php) to enhance the dialog.

Change the logo

First I wanted to replace the WordPress logo by my own logo. Of course you need to first create a cool logo. I didn’t spend too much time on this but I think the ouput is fine. Then you need change the URL of the header background in the login dialog. This is done by enqueuing some CSS when the login dialog is shown. This is done by adding the following to functions.php:

function benohead_login_redesign() { ?>
    <style type="text/css">
        body.login div#login h1 a {
            background-image: url(<?php echo get_stylesheet_directory_uri() ?>/images/benohead.png);
            padding-bottom: 30px;
<?php }
add_action( 'login_enqueue_scripts', 'benohead_login_redesign' );

Note that I use get_stylesheet_directory_url to get the URL of my theme because it’s a child theme. Using get_template_directory_uri() or get_bloginfo(‘template_directory’) would return the directory to the parent theme.

We’ll keep extending the function benohead_login_redesign() with more CSS as we advance in our redesign.

The login dialog now looks like this:
WordPress login logo changed

Change the URL on the logo

Second I wanted to change the URL you are taken to when clicking on the logo. It goes to wordpress.org by default but I wanted it to point to my blog (otherwise changing the logo would make no sense). This can be done by adding the following to the bottom of functions.php:

function benohead_login_headerurl() {
	return get_bloginfo('url');
add_filter('login_headerurl', 'benohead_login_headerurl');

It basically adds a filter used when you click on the header, which replaces the standard URL by the URL of my blog. No screen shot here since there’s nothing to see.

Change it to a dark theme

Since I’m using a dark theme for the blog, I also wanted to have the login page go along those lines, so I changed the background color by adding the following to the CSS in benohead_login_redesign():

        html body.login {
            background-color: #0B0B0B;

It then looked like this:
WordPress login dark

Now the background color is fine but the links at the bottom are not really readable anymore with these text shadows. Let’s remove them:

        .login #nav, .login #backtoblog {
            text-shadow: none;

Now it does look better:
login dark without text shadows

But the dialog itself in the middle is much too bright and should also become darker:

	.login form {
	    background-color: #2B2B2B;

Here’s how it looks like:
WordPress login all dark

Nicer buttons

I wanted to use some nice buttons provided by amazingweb GmbH. They have a nicer shape than the original buttons and elegant and simple effects on mouse hover and when clicked:

	html body.login div#login input#wp-submit.button-primary {
	    display: inline-block;
	    vertical-align: middle;
	    text-align: center;
	    font-family: Helvetica, Arial, sans-serif;
	    font-size: 11px;
	    -webkit-border-radius: 3px;
	    -moz-border-radius: 3px;
	    -o-border-radius: 3px;
	    -ms-border-radius: 3px;
	    -khtml-border-radius: 3px;
	    border-radius: 3px;
	    padding: 5px 15px 5px 15px;
	    width: auto;
	    height: auto;
	    border: none;
	    text-shadow: rgba(0, 0, 0, 0.3) 0px -1px 0px;
	    text-decoration: none;
	    color: #b4b4b4;
	    background: -webkit-gradient(linear, left top, left bottom, from(#00547A), to(#21759B));
	    background: -moz-linear-gradient(top, #00547A, #21759B);
	    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ff00547A, endColorstr=#ff21759B);
	    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ff00547A, endColorstr=#ff21759B)";
	html body.login div#login input#wp-submit.button-primary:hover {
	    color: #b4b4b4;
	    background: -webkit-gradient(linear, left top, left bottom, from(#00648A), to(#2185AB));
	    background: -moz-linear-gradient(top, #21759B, #2185AB);
	    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ff21759B, endColorstr=#ff2185AB);
	    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ff21759B, endColorstr=#ff2185AB)";
	html body.login div#login input#wp-submit.button-primary:active {
	    color: #b4b4b4;
	    background: -webkit-gradient(linear, left top, left bottom, from(#21759B), to(#00547A));
	    background: -moz-linear-gradient(top, #21759B, #00547A);
	    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ff21759B, endColorstr=#ff00547A);
	    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ff21759B, endColorstr=#ff00547A)";

Now this button does look good:
WordPress login new button

Fixing the remember me alignment

Don’t know whether it disturbs anybody else but the alignment of the box and text for the remember me check box just drives me somehow crazy, so I needed to change it right away:

	html body.login div#login form#loginform p.forgetmenot label input#rememberme {
	    vertical-align: baseline;

Now the screenshot:
WordPress remember me realigned

Adapting the registration dialog

Then I noticed that there was still some work to do on the registration dialog:
registration dialog before changes

Here I needed to change two things: change the background color of the message at the top and change the text color below the Email text box (which almost couldn’t be seen):

	html body.login div#login form#registerform p#reg_passmail {
	    color: #777777;
	html body.login div#login p.message {
	    background-color: #2B2B2B;
	    border-color: #777777;
	    color: #777777;

This dialog now looks like this:
registration dialog after changes

Adapting the error dialog

When you enter the wrong credentials an error message is shown in the login dialog:
login error message before changes

And I also wanted to make a few changes:

	html body.login div#login div#login_error {
	    background-color: #2B2B2B;
	    border-color: #FF7777;
	    color: #FF7777;
	html body.login div#login div#login_error a {
	    color: #FF7777;

Here’s how the error message looks like now:
login error message after changes


With these few changes I could dramatically change the look of the login page. That’s one of the things I love about WordPress. It’s so easy to modify anything and even though these kind of things could actually break in a new version of WordPress, it is really rarely the case.

Alternatives to this method are to modify the WordPress PHP files directly (which you should avoid at all costs) or create your own login page and link it. You should opt for the latter only if you need to make such dramatic changes that the method described here cannot be used. And you’ll have to basically implement a copy of the login dialog and will not automatically get fixes when a new version of WordPress is released.

So to summarize, as long as you do not want to completely change the dialog and it’s functionality, you should stick to this method which is easy, relatively upgrade-proof and effective.

WordPress: Hiding the scores in Yet Another Related Posts Plugin

There’s a very nice WordPress plugin called Yet Another Related Posts Plugin (YARPP). It finds related posts, computes a relevance score and displays the X posts with the highest score below your posts.

It looks like this:

Related posts:

  1. WordPress: Create a child theme for twentyeleven with sidebars on posts and posts sorted by modified date (15.5)
  2. WordPress: Add a theme option to display or hide the post author (15.4)
  3. WordPress: The plugin generated 1 characters of unexpected output during activation (5.9)

It’s great but the display of the score disturbed me (it’s just noise to anybody but me) and I wanted to get rid of it. I searched in the plugin options and couldn’t find anything and just gave up. Today I just happened to open one of my posts with the wrong browser. Since I was not logged in automatically, I could actually see the post the way it looks like to my visitors. And there was the solution ! The scores were gone ! So it actually only displayed the scores because I was logged in. So the answer to the question how the scores could be removed is very simple: they do not have to be removed because nobody but me will see them !