Protect WordPress Against Malicious URL Requests

Post #732 categorized as WordPress, last updated on Apr 5, 2010
Tagged with blacklist, htaccess, php, plugins, security, url, WordPress

A few months ago, many WordPress sites were attacked with some extremely malicious code. While searching for a good solution, I discovered the following gem of a plugin in the pastebin repository:

<?php /* Plugin Name: Block Bad Queries */

if (strlen($_SERVER['REQUEST_URI']) > 255 || 
	strpos($_SERVER['REQUEST_URI'], "eval(") || 
	strpos($_SERVER['REQUEST_URI'], "base64")) {
		@header("HTTP/1.1 414 Request-URI Too Long");
		@header("Status: 414 Request-URI Too Long");
		@header("Connection: Close");
		@exit;
} ?>

This script checks for excessively long request strings (i.e., greater than 255 characters), as well as the presence of either “eval(” or “base64” in the request URI. These sorts of nefarious requests were implicated in the September 2009 WordPress attacks.

To protect your site using this lightweight script, save the code as a plugin and activate in the WordPress Admin area. Once active, this plugin will silently and effectively close any connections for these sorts of injection-type attacks.

For further protection against malicious code, automated attacks, and other cracker nonsense, check out my 4G Blacklist.

Update to original method

As is often the case, Perishable Press readers have helped to improve this plugin by leaving comments, asking questions, and recommending changes. Here is the new, recommended version of the plugin:

<?php 
/* 
Plugin Name: Block Bad Queries
Plugin URI: http://perishablepress.com/press/2009/12/22/protect-wordpress-against-malicious-url-requests/
Description: Protect WordPress Against Malicious URL Requests
Author URI: http://perishablepress.com/
Author: Perishable Press
Version: 1.0
*/
global $user_ID; if($user_ID) {
	if(!current_user_can('level_10')) {
		if (strlen($_SERVER['REQUEST_URI']) > 255 || 
			stripos($_SERVER['REQUEST_URI'], "eval(") || 
			stripos($_SERVER['REQUEST_URI'], "CONCAT") || 
			stripos($_SERVER['REQUEST_URI'], "UNION+SELECT") || 
			stripos($_SERVER['REQUEST_URI'], "base64")) {
				@header("HTTP/1.1 414 Request-URI Too Long");
				@header("Status: 414 Request-URI Too Long");
				@header("Connection: Close");
				@exit;
		}
	}
} ?>

The changes include, in order, proper plugin declaration (thanks Aldo), exclusion of admin pages, and additional protection against CONCAT and UNION+SELECT requests (thanks John Hoff).

Update [March 5th, 2010]: Block Bad Queries is now available at the Plugin Repository.

Update [April 5th, 2010]: Code changed to use stripos instead of strpos. See this comment for details. Thanks to James Wilkes for the tip :)

Subscribe to Perishable Press


85 Responses

TopLeave a comment

[ Gravatar Icon ]

#1Alex Denning

Great tip Jeff. Thanks for that. Going into my framework :)

[ Gravatar Icon ]

#2Eric B.

Very useful security tip. Thanks for sharing!

[ Gravatar Icon ]

#3Indrek

Thanks a lot for this plugin. You can never have a too secure blog so this is definitely going to be added on mine.

[ Gravatar Icon ]

#4Rick Beckman

Even better, just add it to the top of wp-config.php. It’s loaded before plugins are (I think), so handling as much security as possible there cuts down on the amount of processing that needs to be done to handle invalid/malicious requests.

It also keeps the plugin panel clean. :D

[ Gravatar Icon ]

#5Aldo

Merry Christmas, Jeff, and thanks again for your precious posts. :-)

[ Gravatar Icon ]

#6Lillan

Useful indeed! Tnx :)

[ Gravatar Icon ]

#7Thomas Scholz

Most attackers try to load some external scripts into the page. My double slash fix (http://toscho.de/2009/wordpress-2-8-3-das-doppelslash-problem/) has a nice side effect here: it makes all URLs in GET parameters invalid.

@Rick Beckman: If you call this code before WordPress has fixed the broken REQUEST_URI on IIS it won’t work there.

[ Gravatar Icon ]

#8Rick Beckman

I was unaware that WordPress did any fixing of URLs for IIS’ sake. If that’s the case, then the plugin would be better — or emulating the fix in the security code, if it’s concise enough to copy/paste easily.

[ Gravatar Icon ]

#9Greg

Nice one

the too long request limit could be done via htaccess with something like this:

RewriteCond %{REQUEST_URI} ^(.*){255,} [NC]
RewriteRule - [R=414,L]

I bet…?

Just wonder if this is not implemented by default on apache?

[ Gravatar Icon ]

#10JP

Is this something everyone should implement, or are only pre-WP 2.9 blogs vulnerable?

[ Gravatar Icon ]

#11tcg

I could be wrong, but I’m pretty sure this may break a couple of cases:

For instance, if I’m using permalinks that contain the article title, and my article was called “About Base64″, by default Wordpress would use something like “about-base64” as the slug for my post.

If someone tries to visit that page, doesn’t that put the string “base64” somewhere in the Request URI?

Just wondering/noting.

[ Gravatar Icon ]

#12Sebastian

If you plan to do any bulk management of your posts, I’d recommend putting this code inside

if(!is_admin()) { /* your code here */}

[ Gravatar Icon ]

#13Yael K. Miller

I’m a little confused with that to do with this. Is the below correct?

  1. paste code into new file
  2. save as blockbadqueries.php
  3. upload to wp-content/plugins
  4. will appear in dashboard as a plugin - activate it.
[ Gravatar Icon ]

#14Jeff Starr

Thanks everyone for the great feedback on this post. To respond to a few of the questions:

@Greg: Good call. Perhaps not set by default on Apache, but custom-configured on certain servers. Btw, is {255,} proper syntax? Looks odd..

@JP: As far as I know, WordPress fixed the vulnerability in the Admin area, but I don’t think it protects against the malicious URL requests blocked in this plugin.

@tcg: yep, if you plan on blogging about Base64, you’ll need a conditional statement in there to allow for the permalink. Fortunately (or unfortunately, depending on how you look at it), Base64 is a rather rare topic for blog posts ;)

@Sebastian: Good idea :)

@Yael K. Miller: yes, that is exactly correct. And once the plugin is activated, nothing else needs to be done. It works quietly in the background to block the bad guys. “Fix it and forget it,” as they say.

[ Gravatar Icon ]

#15Greg

@Jeff Starr Hi Jeff, as you said yourself:

a{n,} specifies n or more of the preceding character. e.g., x{3,} matches three or more x’s.” so I presume that: if I match “whatever” with the “^(.*)” and set the max number or more with {255,} (comma mean “or more” here) it could be ok.

My own server is restrict by 36 characters for a filename, so you probably right, this is apache related, because unix by default is set to 255…but this doesn’t mean there’s a limit for the uri.

I’m testing more of that stuff and give feedback asap. :)

[ Gravatar Icon ]

#16John Hoff - WP Blog Host

Excellent tip, Jeff. One question though.

It looks as if your 4g Blacklist already does this through .htaccess. Is that not true?

Thanks

[ Gravatar Icon ]

#17Jeff Starr

Hi John, Good question.. The 4G Blacklist does not target either of these requests specifically, but it does block most of the junk that is used within these types of requests. I.e., if you are using the 4G Blacklist, this plugin is probably overkill, but certainly won’t hurt your security efforts ;)

Also, the upcoming 5G Blacklist will block these requests specifically.

[ Gravatar Icon ]

#18John Hoff - WP Blog Host

Jeff, could this code be altered to include such strings as CONCAT and UNION+SELECT?

[ Gravatar Icon ]

#19Shay Howe

Jeff you have some amazing articles and this one is no exception. This is a great plugin, thank you for your forward thinking!

I did however experience one issue. Using this plugin causes the included WordPress jQuery file (../wp-includes/js/jquery/jquery.js) to stop working properly. I rely on the included jQuery file for a few features on my website and when I added your plugin those features stopped working. Instead I started getting the “Request-URI Too Long” error.

Just a heads up, thanks again for your good work!

[ Gravatar Icon ]

#20Jeff Starr

@John Hoff: Yes of course - perhaps something like this would do it:

<?php /* Plugin Name: Block Bad Queries */

if (strlen($_SERVER['REQUEST_URI']) > 255 ||
       strpos($_SERVER['REQUEST_URI'], "eval(") ||
       strpos($_SERVER['REQUEST_URI'], "CONCAT") ||
       strpos($_SERVER['REQUEST_URI'], "UNION+SELECT") ||
       strpos($_SERVER['REQUEST_URI'], "base64")) {
              @header("HTTP/1.1 414 Request-URI Too Long");
              @header("Status: 414 Request-URI Too Long");
              @header("Connection: Close");
              @exit;
} ?>

[ Gravatar Icon ]

#21Jeff Starr

@Shay Howe: Thanks for the heads up! Not sure why that might be happening in your case, but perhaps an equivalent htaccess method would work instead?

[ Gravatar Icon ]

#22Shay Howe

@Jeff Starr: Thanks for your suggestion, I appreciate your help. I tried dropping it into my .htaccess file via:

RewriteCond %{REQUEST_URI} ^(.*){255,} [NC]
RewriteRule - [R=414,L]

This still blocked the jQuery file and also through off my CSS file. I tried using your 4G Blacklist as well and encountered the same issue with the jQuery file.

I’ll play with this a little more and let you know if I am able to get it working correctly. Thanks Jeff!

[ Gravatar Icon ]

#23Aldo

Helpful plugin, as I wrote before, but it doesn’t allow operations as, for example, making a bulk/mass edit of posts.

I was editing a couple of posts by adding a tag: the result was a blank page. Looking at the address bar and the long URL, I instantly realized that this plugin doesn’t allow such URLs. I deactivated it and was able to make the bulk edit.

[ Gravatar Icon ]

#24Jeff Starr

Hi Aldo, assuming we can safely allow long query strings in the WordPress Admin area, we can do something like like this:

<?php /* Plugin Name: Block Bad Queries */

if (!current_user_can('level_10')) {
       if (strlen($_SERVER['REQUEST_URI']) > 255 ||
              strpos($_SERVER['REQUEST_URI'], "eval(") ||
              strpos($_SERVER['REQUEST_URI'], "base64")) {
                     @header("HTTP/1.1 414 Request-URI Too Long");
                     @header("Status: 414 Request-URI Too Long");
                     @header("Connection: Close");
                     @exit;
       }
} ?>

This should allow you to do everything you normally do in the Admin area (mass-editing of posts, etc.), while still protecting the external site from attacks.

[ Gravatar Icon ]

#25Aldo

Hi Jeff, right! :-)
Thanks!

This plugin should go in the wordpress repository.

[ Gravatar Icon ]

#26Yael K. Miller

Jeff, what’s your recommendation for use? The plugin you gave in the post or what you left in comment #24 ?

[ Gravatar Icon ]

#27Jeff Starr

Yael, go with the plugin given in comment #24. I need to update the post with the improved code.

[ Gravatar Icon ]

#28John Hoff - WP Blog Host

I tried the one in #24 and it gave me a fatal error on my blog.

If you’re going to use that one, maybe test it a couple times.

For now, the code in #20 does well for me. Thanks, Jeff.

[ Gravatar Icon ]

#29Jeff Starr

The post has been updated with better code. The new version allows for long URIs in the Admin area, blocks two additional request strings, and should work in all versions of WordPress. Please use the new plugin instead, especially if you experienced issues with the previous version.

[ Gravatar Icon ]

#30John Hoff - WP Blog Host

Thanks for the mention, Jeff. Also, the new updated code did not give me a fatal error … works like a charm!

Thanks again.

[ Gravatar Icon ]

#31Rod Homor

Nice! I always appreciate folks willing to share ideas for keeping WordPress as safe as possible, b/c these days, who doesn’t have a client project running on WP??

Thanks!

[ Gravatar Icon ]

#32elcidi

This is very much helpful to increase the security.
Many thanks foe sharing it

[ Gravatar Icon ]

#33Jauhari

I will install on my Blog ;)

[ Gravatar Icon ]

#34cubus

Wouldn’t it be possible to include this in the functions.php file of your wordpress (framework) theme?
That way I don’t have to think of including it as a plugin in every wordpress set-up I do.

[ Gravatar Icon ]

#35Rick Beckman

Yes, that should work, cubus. I prefer to do all sorts of customizations via my theme’s custom functions file (either that or directly to the wp-config.php file, just for kicks).

[ Gravatar Icon ]

#36ikram

thanks. very usefull

I will use it on my blog

regards

[ Gravatar Icon ]

#37r-a-y

Great code.

Just wondering if, in certain scenarios, a permalink can be longer than 255 characters.

I can think of a weird example where there’s a bunch of recursive sub-categories and a long %postname%.

This is if someone is using /%category%/%postname%/ as their permalink structure.

[ Gravatar Icon ]

#38Jeff Starr

@r-a-y: I’m sure that is a possibility, but I have never actually seen a permalink that long before. It’s a good heads up for others though: if your site tends to include more than 255 characters in the permalinks, just edit the associated line as follows:

if (strlen($_SERVER['REQUEST_URI']) > 255 ||

Replace the “255” with a larger number, like maybe 355 or something. The idea is to catch the malicious URL requests that tack on long strings of exploitative cargo.

[ Gravatar Icon ]

#39mrkay

Thanks for great security tip for WordPress. I`ll implement in my blog and write a blogpost for this tip.

[ Gravatar Icon ]

#40Frank

hello Jeff,
i had include your script in my plugin Secure WordPress - is this ok for you?
Thanks for your reply adn nice work.

[ Gravatar Icon ]

#41Jeff Starr

@Frank: Of course, we’re on the same team :) Hopefully it further help your already excellent plugin. Cheers!

[ Gravatar Icon ]

#42james wilkes

Hi there. I’m struggling to learn about Wordpress security and reading everything I can find on the subject. I’ve a question (please forgive me if it’s a stupid one!) : the CONCAT and UNION+SELECT are SQL vulnerablilities aren’t they? So wouldn’t it be better to use stripos (case-insensitive) rather than strpos since SQL isn’t case-sensitive is it?

Great article anyway! I’ll be trying this out.

[ Gravatar Icon ]

#43Jeff Starr

@james wilkes: Excellent suggestion. I’ll check it out and get it changed in the next update. Here is the plugin at WordPress.org: http://wordpress.org/extend/plugins/block-bad-queries/

Thanks for your help! :)

Note: This article has been updated with the new code.

[ Gravatar Icon ]

#44Deepesh Divakaran

That was awesome. I still get such long URL Requests, which I see from my google analytics acct, and used to wonder what the hell these are.
This will help me definitely. Thanks a ton.

[ Gravatar Icon ]

#45tbdp

we also use this code below in all our HTACCESS files — do you think it would help prevent the current sql base 64 etc injections as well as the info you reference above?

# STRONG HTACCESS PROTECTION
<Files ~ "^.*\.([Hh][Tt][Aa])">
order allow,deny
deny from all
satisfy all
</Files>
<Files .htaccess>
order allow,deny
deny from all
</Files>
<FilesMatch "\.(htaccess|htpasswd|ini|phps|fla|psd|log|sh)$">
Order Allow,Deny
Deny from all
</FilesMatch>
# end STRONG HTACCESS PROTECTION

[ Gravatar Icon ]

#46NavaPavan

Thanks for sharing. Will make in my directory right now

[ Gravatar Icon ]

#47Alex

Your surname is highly appropriate, Mr Starr!

Best from Italy,

Alex

[ Gravatar Icon ]

#48gogobilo

thank you.. this post is very useful for me :)

[ Gravatar Icon ]

#49Satya Prakash

I have tried using it but it does not work.
I have activate it also.
I have added eval(), base64 in request uri but that does not given anything new. no effect at all

[ Gravatar Icon ]

#50Pomy

can i use this plugin for bbpress? (bbpress.og)

[ Gravatar Icon ]

#51_ck_

Here’s the thing, I wrote that original mini-plugin that you posted:

http://ckon.wordpress.com/2008/11/11/simple-wordpress-and-bbpress-security-plugin-to-block-long-requests/

But your modification defeats the entire original purpose.

Your code only runs if there is an active user, if someone is not logged in and there is a loophole, the code would run unchallenged.

Plus your exclude admin, so if an admin account gets hacked, or if there is compromised code trying to send an admin cookie elsewhere, what you did makes sure the code is bypassed, defeating all safety.

[ Gravatar Icon ]

#52Nathan

when you have fast and secure contact form installed it prints out errors because output is sent before it gets to this plugin. WP3.0

http://www.642weather.com/weather/scripts-wordpress-si-contact.php

[ Gravatar Icon ]

#53Jeff Starr

_ck_, Awesome, thanks for writing this plugin. Using the original code, people were reporting admin/plugin errors, so Admin pages were excluded. The script seems to work great otherwise, but the user thing is an obvious mistake. What do you think is the best path forward? Would you like to take over updating of the plugin at the repository?

[ Gravatar Icon ]

#54_ck_

I think some are getting errors in admin because newer versions of WordPress and some plugins use extra long query strings.

What you could do is allow up to 512 instead of 256 if the user is a valid admin.

Part of the problem is of course that WP uses the admin section for even regular users (ie. to manage subscriptions)

Making it 512 for everyone unfortunately might negate some of the effectiveness of the plugin.

[ Gravatar Icon ]

#55Jeff Starr

@Satya Prakash: That may be because of the way the plugin is currently configured. It will updated soon with better functionality.

@Pomy: Yes, should work fine with bbPress.

@Nathan: Will look into it, thanks for the information.

@_ck_: I have some other changes I would like to make (in addition to the user/admin stuff), so if all goes well things should be improved for the next version. The beauty of this plugin is in its simplicity and effectiveness.

[ Gravatar Icon ]

#56Joomla Tutorials

Excellent tip, Jeff. One question though.
It looks as if your 4g Blacklist already does this through .htaccess. Is that not true?

Thanks

[ Gravatar Icon ]

#57Jeff Starr

@Joomla Tutorials: Essentially, yes - the protection is going to be similar, although through different mechanisms. For example, the 4G blacklist doesn’t block long strings, but the BBQ script does. But the 4G matches tons of other stuff that this script doesn’t, including the longer requests as they typically contain at least one or more matching strings.

Bottom line: the 4G is superior to this script :)

Trackbacks / Pingbacks

  1. Twitter Trackbacks for Protect WordPress Against Malicious URL Requests • Perishable Press [perishablepress.com] on Topsy.com
  2. uberVU - social comments
  3. Protect WordPress Against Malicious URL Requests • Perishable Press | Rumball Motors Interactive
  4. Protect WordPress Against Malicious URL Requests • Perishable Press « Netcrema – creme de la social news via digg + delicious + stumpleupon + reddit
  5. MUST READ Wednesday 12/23/09 | Thoughts about Nothing*com
  6. delicious Links: 23. December 2009
  7. GNU GPL malware : Troj/JSRedir-AK « Documentation Mainframe
  8. Proteggere WordPress da richieste URL maligne ‹ Ubuntu block notes
  9. Hacked « wongaBlog
  10. Plugging In, Part II | DMLaBadie
  11. Descubrimientos del 23 Diciembre 2009 | Blog de unique3w
  12. 9 Vital Tips to Secure Wordpress | Security Attack
  13. 7 Easy Ways to Secure Your WordPress Site « JACKSON MURPHY
  14. 10 NEW WordPress Wanted Hacks and Powerful Techniques » DevSnippets
  15. 10 Steps to WordPress Security Protection | The Web Mechanic
  16. Lindungi URL Wordpress Dari Request Berbahaya
  17. Protect WordPress Against Malicious URL Requests | KSSP
  18. 6 Easy Ways to Secure Your WordPress Site | JACKSON MURPHY
  19. Tips Keselamatan Blog | Orange 4 Kay
  20. WordPress plugin: Protect your blog from malicious URL Requests
  21. WordPress: New Plugin Blocks Malicious URL Requests - The SEO News Blog with Pat Marcello
  22. How to Add Your Plugin to the WordPress Plugin Directory | Digging into WordPress
  23. WordPress Security Hacks
  24. thanks to these folks « Fried Chicken and Okra
  25. 10 Useful WordPress Security Tweaks - Smashing Magazine
  26. Membuat Plug-In untuk Proteksi Blog dari Malicious URL | Belajar Komputer
  27. WordPress Security Lockdown | Digging into WordPress
  28. Block Bad Queries - Wordpress Security Plugin | Blog Muria

Share your thoughts..

TopRead official comment policy

The rules are simple. Comment intelligently. Stay on-topic. Don’t spam! Suspected spam will be deleted. Use your real name or nickname, not a site name or business name. Using a site name or business name is a good way to get your link or comment removed. Certain comments are moderated; if your comment does not appear after several days, or if you wish to comment privately, contact me. Also, by posting a comment, you grant this site a perpetual license to reproduce your comment, name, and website URL. Lastly, you may use basic HTML markup, but please do not use <pre> tags. Instead, wrap your code with <code> tags. Use a new set of <code> tags for each code term or phrase, as well as for each individual line of code (i.e., multiple lines of code require multiple code tags). Please see the complete comment policy for more information.