Category: Wordpress

parse_url Warning Bug In Wordpress 2.9.2

23 February, 2010 | Wordpress | 2 comments

Whilst setting up a new Wordpress install for a site development I found an odd little issue that would only occur in certain circumstances. My current development platform consists of an Apache server on which I create a virtual host for every development site I need. However, rather than setting a DNS entry for each address I just listen to diferent ports. In this particular instance I used the port 59419.

When the install was complete I was presented with the following error at the top of every admin page.

Warning: parse_url(http://10.0.0.1:594191?ver=20091217) [function.parse-url]: Unable to parse URL in \www\wp-includes\script-loader.php on line 542

For some reason Wordpress was adding a "1" to the end of the port number, which was causing the URL to throw a warning message when calling parse_url() as the port number can't be higher than about 65535 (depending on the system). This normally wouldn't cause an issue on most setups, but in this case it increased the port number just high enough to throw this error.

Although the warning is issued on line 542 of wp-includes\script-loader.php, I actually tracked the offending code down to line 438 in the same file. Here it is, along with the comment above it:

// Register "meta" stylesheet for admin colors. All colors-* style sheets should have the same version string. $styles->add( 'colors', true, array(), $colors_version );

The true value is normally a CSS filename, but this one is needed to set some initial conditions for the rest of the admin styles. So when this is appended to the base path it is converted to 1, which is what normally happens when you echo a true boolean value in PHP. To solve this issue I just replaced this true value with a string consisting of a slash.

$styles->add( 'colors', '/', array(), $colors_version );

This has no effect on the functionality of this code. Removing this line completely will cause the admin area to lose all colours and images.

Although this is a very minor bug, it must have caused an issue on someone's system at some point in the past. So I put this fix here in case anyone else has had the same issue.

Written by Philip Norton.

Wordpress Breadcrumbs

19 December, 2009 | Wordpress | No comments

I have been playing about with breadcrumb trails in Wordpress recently so I thought I would post the code here in case anyone else finds it useful. The basic idea is to try to find out where the current page is situated within the site. This means checking for pages and categories with their parents. Once we get down to the post level we need to try some things in order to find out where the current post is within the site.

Once the trail array has been filled with items it is looped through and converted into a string.

To use this function put the following code into your functions.php file in your template.

/**  * Echo or return a formatted list of breadcrumbs.  *  * @param  array  $args An array of arguments to controll the output of the   *                      function.  * @return string       The breadcrumb list.  */ function get_breadcrumbs($args = array()) {     global $post;         if (is_string($args)) {         parse_str($args, $args);     }       // Set up defaults.     $defaults = array(         'separator'   => ' &gt; ',         'linkFinal'   => false,         'echo'        => true,         'printOnHome' => true,      );       // Merge the defaults with the parameters.     $options = array_merge($defaults, (array)$args);       // Initialise the trail with the current post.     $trail = array($post);          // Initialise the output.     $output = '';          $currentCategory = 0;       if (is_front_page() == true && $options['printOnHome'] == false) {         /**          * If this is the front page and the option to prevent priting on the          * home page is disabled then echo or return the empty string depending          * on the echo option.          */         if ($options['echo'] == true) {             echo $output;         }         return $output;     }          if (is_page()) {         // If the current page is a page.         $parent = $post;         while ($parent->post_parent) {             $parent = get_post($parent->post_parent);             array_unshift($trail, $parent);         }     } elseif (is_category()) {         // The current page is a category page.         $trail = array();         $currentCategory = get_query_var('cat');         $category        = get_category($currentCategory);         while ($category->category_parent > 0) {             array_unshift($trail, $category);             $category = get_category($category->category_parent);         }         // Add the final category to the trail.         array_unshift($trail, $category);     } else {         // The current page will be a post or set of posts.         $path = explode('/', $_SERVER['REQUEST_URI']);         $path = array_filter($path);         while (count($path) > 0) {             $page = get_page_by_path(implode('/', $path));             if ($page != NULL) {                 array_unshift($trail, $page);             }             array_pop($path);         }                  if (count($trail) == 1) {             // No pages found in path, try using categories.             $category = get_the_category();             $category = $category[0];             while ($category->category_parent > 0) {                 array_unshift($trail, $category);                 $category = get_category($category->category_parent);             }             array_unshift($trail, $category);         }     }       $show_on_front = get_option('show_on_front');     if ('posts' == $show_on_front) {         // The home page is a list of posts so just call it Home.         $output .= '<li class="breadcrumb-item" id="breadcrumb-0"><a href="' . get_bloginfo('home') . '" title="Home">Home</a></li>' . "\n"; // home page link     } else {         // Otherwise the front page is a page so get the page name.         $page_on_front = get_option('page_on_front');         $home_page = get_post($page_on_front);         $output .= '<li class="breadcrumb-item" id="breadcrumb-' . $home_page->ID . '"><a href="' . get_bloginfo('home') . '" title="' . $home_page->post_title . '">' . $home_page->post_title . '</a></li>' . "\n"; // home page link         if ($trail[0]->ID == $page_on_front) {             array_shift($trail);         }     }       if (is_front_page() == false) {         // If we aren't on the home page then construct the output.          foreach ($trail as $key => $page) {             // Every item in the trail will be either a post/page object or a category.             if (count($trail) - 1 == $key && $options['linkFinal'] == false) {                 // If we are on the last page and the option to link the final link is true.                 if (isset($page->post_title)) {                     $output .= '<li class="breadcrumb-item" id="breadcrumb-' . $page->ID . '">' . $options['separator'] . ' ' . $page->post_title . '</li>' . "\n";                 } elseif (isset($page->cat_name)) {                     $output .= '<li class="breadcrumb-item" id="breadcrumb-cat-' . $page->term_id . '">' . $options['separator'] . ' ' . $page->cat_name . '</li>' . "\n";                 }             } else {                 // Create the link to the page or category                 if (isset($page->post_title)) {                     $output .= '<li class="breadcrumb-item" id="breadcrumb-' . $page->ID . '">' . $options['separator'] . '<a href="' . get_page_link($page->ID) . '" title="' . $page->post_title . '">' . $page->post_title . '</a></li>' . "\n";                 } elseif (isset($page->cat_name)) {                     $output .= '<li class="breadcrumb-item" id="breadcrumb-cat-' . $page->term_id . '">' . $options['separator'] . '<a href="' . get_category_link($page->term_id) . '" title="' . $page->cat_name . '">' . $page->cat_name . '</a></li>' . "\n";                 }             }         }     }          // Finish off the html of the ul     $output = "<ul>\n" . $output . "</ul>\n";          if ($options['echo'] == true) {         // Print out the $output variable.         echo $output;     }     // Return      return $output; }

Print out the breadcrumb trail on your page like this:

get_breadcrumbs();

Several options are also available to change the way the function works. These are as follows:

  • separator: The string to separate the items in the breadcrumb trail, defaults to &gt;.
  • linkFinal: Whether or not to print the last link as a link. Defaults to false.
  • echo: Whether or not to print out the breadcrumb in the function. Defaults to true.
  • printOnHome: Print the breadcrumb on the home page. Defaults to true.

You can set the options for the get_breadcrumbs() function in much the same way as normal Wordpress functions. This can be through a parameter string or a array like this.

<?php get_breadcrumbs(     array(         'separator'   => ' &gt; ',         'linkFinal'   => false,         'echo'        => true,         'printOnHome' => false,      ) ); ?>

This will print out something like the following:

<ul>  <li class="breadcrumb-item" id="breadcrumb-0"><a href="http://www.example.com/wordpress_test" title="Home">Home</a></li>  <li class="breadcrumb-item" id="breadcrumb-174"> &gt; <a href="http://www.example.com/wordpress_test/level-1" title="Level 1">Level 1</a></li>  <li class="breadcrumb-item" id="breadcrumb-173"> &gt; <a href="http://www.example.com/wordpress_test/level-1/level-2" title="Level 2">Level 2</a></li>  <li class="breadcrumb-item" id="breadcrumb-172"> &gt;  Level 3</li>  </ul>

Because Wordpress is one of those applications that can be set up in a number of different ways I have tried to account for as many situations as I can. However, if you try this breadcrumb function out and it doesn't work as expected post a comment here and let me know.

Written by Philip Norton.

Change Number Of Search Results In Wordpress

27 November, 2009 | Wordpress | 5 comments

Wordpress allows administrators to change the number of posts that are printed out on a page at once on the page Settings > Reading. However, this also has an impact on the number of search results that Wordpress will display. In order to keep the size of the home page down many Wordpress admins will reduce the number of articles to two or three. This can instantly make search results with only a few results reach many pages.

To get around this you will need a template called search.php, this is the template that Wordpress will use when printing out the search results. Towards the top of this file (anywhere above The Loop) add the following code.

<?php query_posts($query_string . '&showposts=10'); ?>

The $query_string variable is the currently used query string that generated the search results. Just set the value of the showposts variable to be the number of posts you want to display in the search results.

Written by Philip Norton.

Wordpress DoS Attack Script Solution

20 October, 2009 | Wordpress | 13 comments

There is a script knocking about on the internet at the moment that allows an attacker to run some code that will bring your Wordpress blog to it's knees. This will more than likely cause your host to get annoyed as well.

What it does it performs a trackback request to the file wp-trackback.php, but it sends a massive (over 200,000 characters) string that Wordpress will take at face value and accept as a legitimate trackback. The first time this is run Wordpress will write it to the database, but the every time after that it will run a select query to see if the trackback exists. Even though this isn't a legitimate trackback Wordpress will still process it on every request, causing a massive overhead as each large string is processed.

One solution is to simply stop access to the offending file by using an Apache rule in your .htaccess file to prevent all access to this file.

<Files ~ "wp-trackback.php"> Order allow,deny Deny from all </Files>

This might be a good solution if you don't care about trackbacks, but if you want them then there exists another fix that involves changing the wp-trackback.php file to exit if the charset if quite long. Open the file and look for line 47.

$charset = $_POST['charset'];   // These three are stripslashed here so that they can be properly escaped after mb_convert_encoding()

Add the following lines:

$charset = $_POST['charset'];   // DoS attack fix. if ( strlen($charset) > 50 ) {  die; }   // These three are stripslashed here so that they can be properly escaped after mb_convert_encoding()

This will stop any script kiddie from running the exploit script and killing your blog, but you might also want to add a check to make sure that the title variable isn't a stupid size as well. On line 56 (assuming you implemented the previous fix) add the following lines.

if ( strlen($title) > 200 ) {  die; }

This assumes that the trackback's post won't be longer than characters. There might be some legitimate posts that do have titles this long, but it is my opinion that these are probably poor quality posts and so okay to ignore.

Many thanks to Steve Fortuna's post New 0-Day Wordpress Exploit for pointing out and providing a fix to this issue.

UPDATE

Wordpress released an update this morning (V2.8.5) to fix this and other exploits. As usual Wordpress are quick in releasing patches for fixing exploits and vulnerabilities.

Written by Philip Norton.

Wordpress 2.8.3 Admin Password Exploit And The Fix

11 August, 2009 | Wordpress | 3 comments

A small exploit has been found within the latest (currently 2.8.3) version of Wordpress that can cause any attacker to reset the admin password. The attacker won't be able to see what the password is, and the new password is emailed to you anyway, but it might cause some users to be locked out of their blogs if they can't get access to the email or their email is down. However, it is a real pain so I am writing this blog to allow other bloggers to fix their Wordpress installs.

The Problem

Wordpress allows you to reset the admin password, which some users might want to do. The normal course of events is that the admin user selects that they want to reset their password and Wordpress will email the user a link containing a key. Following this link will reset the admin password. The issue is that it is possible to recreate this link without first sending out the email, which will reset the password. Here is the URL that can be used to reset the password.

http://DOMAIN_NAME.TLD/wp-login.php?action=rp&key[]=

I include this here so that you can make sure that the fix below has worked. Please don't try it out on other people's blogs!

The Solution

The solution is to reject activation keys that are arrays, but in case you don't follow what is going on in this trac report all you need to do is open up the file wp-login.php in the root of your blog and change line 190 from this: if ( empty( $key ) )

To this:

if ( empty( $key ) || is_array( $key ) )

Upload the file to your site and you are done! You can check that this has fixed the issue by visiting the link above, you will see an invalid link notice.

Written by Philip Norton.