Anti-Spamming the MirthCanal
Posted in: Development, Personal
The MirthCanal is a site by David Wong (not his real name, methinks) who also runs Pointless Waste of Time (PWoT), a comedy site and forum. I’ve been visiting for many years, and have spent many hours, tears streaming from my eyes, doubled over and unable to breathe from the Funny.
The idea for the MirthCanal started in the forums, where people would inevitably post links to other sites they found had the Funny. So David got Matthew “Spaceboy” Taylor, another forum regular, to create the MirthCanal, so that people could post their links which would then be voted on by other visitors and the funniest links would migrate to the top every week.
Recently, the MirthCanal became a target for a bunch of Viagra spambots. The mods were spending most of their time deleting hundreds of spam links instead of doing the the good mod tasks of making sure that all NSFW links were marked and such. David started a thread in the forums asking for suggestions and help, and I suggested using a CAPTCHA image and also recommended using Peter’s Custom Anti-Spam Image Plugin for Wordpress as a basis (no need to re-invent the wheel afterall). There was some discussion about it being too much work and that the plugin wouldn’t work out of the box (no kidding), and eventually I said I would do it.
So, dusting off my meager PHP skills, I set to work. During the last week, I spent some evenings downloading copies of the MirthCanal database and source code, and setting it up on my development box so that I could mess with it. I also downloaded the latest version of Peter’s plugin as the base for the CAPTCHA.
Yesterday afternoon I sat down to do the actual implementation. Peter has heavily commented his code, and it was a pleasure to go through it and strip out the bits I needed. Mathew used typical PHP style with short concise files and requires and includes that made sense, and it was easy to find the places where I needed to inject code to pull this all together.
In the end I separated Peter’s code into two files (his code is a single file which the WordPress plugin system knows what to do with, there is no plugin system on the MirthCanal): one containing all the settings, which could then be required where, well, required, and the other one does the actual image rendering and is the target of a img src tag. I added a few additional settings so that David could change things like the label, the hint text, and the error message when the incorrect word is entered. For consistency, I followed Peter’s style of prefixing all relevant variables with $cas_ (Custom Anti-Spam, I imagine). In addition, I marked all the places that I injected code with the comment //Added for antispam.
The net result is this: The config file (anticonfig.php):
//antispam.php
//Author: Marc "Mr Moo" Heiligers
//Anti-spam image code modified from:
// Version 2.6: Peter’s Custom Anti-Spam Image Plugin for Wordpress
// (http://www.theblog.ca/?p=21)
//'cause my PHP skillz are not up to scratch.
//I have separated the settings into the anticonfig.php file for easier
//messing with and including into the submit.php file
// -----------------------------------------------------------------------------------
// Customize these parts if you want to change the defaults.
$cas_text = array(); // <-- This line should not be changed
// List as many words as you like, one per line
// If you want some words to be used more often, enter them multiple times.
// It is best to use words that are seven letters or less so that they don't get
// cut off (otherwise, increase the default image size a bit further down the code)
$cas_text[] = "ballz";
//.
//. Additional words removed for brevity
//.
$cas_text[] = "paxton";
// Set the path to the font(s). Example: "wp-content/fonts/"
// Empty string ("") defaults to the root of your blog.
// If you add a path, DO include the trailing slash.
$cas_fontpath = "";
$cas_fontlist = array(); // <-- This line should not be changed
// List as many TrueType font(s) as you like, one per line.
// Drop your own font files into this plugin's directory.
// If you are using your own fonts, make sure all fonts used are about the same default size.
// If you want some fonts to be used more frequently, enter them multiple times.
// Default freeware fonts from font101.com
$cas_fontlist[] = "jenkinsv.ttf";
$cas_fontlist[] = "beatty.ttf";
$cas_fontlist[] = "ateliersans.ttf";
// Set the anti-spam image width and height.
// You may need to increase these sizes for longer words and/or bigger fonts.
$cas_imgwidth = 160;
$cas_imgheight = 50;
// Set this to TRUE if you want to use random text colors.
// If random colors are not selected, blue text will appear on a white background, and white
// text will appear on black background (as decided in the next option)
$cas_randomcolors = true;
// Set the background color for the anti-spam image.
// Choose either "black" or "white"
$cas_bgcolorset = "white";
// Set the border color for the anti-spam image.
// Write either major colors (red, green, blue, etc.)
// or enter the HTML color code (such as #C0C0C0)
$cas_borderclr = "black";
// Set this to TRUE if you prefer PNG graphics (better quality text)
// Set this to FALSE if you prefer more compatable graphics (PNG crashes IE 4; JPEG does not)
$cas_UsePngNotJpeg = false;
// Set this to the title for the antispam field
$cas_fieldTitle = "Are you human?";
// Set this to the hint for the antispam field
$cas_hintText = "Copy the word from the image into this box";
// Set this to the error message you'd like displayed when
// someone gets the antispam message wrong
$cas_errorMessage = "You ain't human.";
?>
The image renderer (antispam.php):
//antispam.php
//Author: Marc "Mr Moo" Heiligers
//Anti-spam image code modified from:
// Version 2.6: Peter’s Custom Anti-Spam Image Plugin for Wordpress
// (http://www.theblog.ca/?p=21)
//'cause my PHP skillz are not up to scratch.
//I have separated the settings into the anticonfig.php file for
//easier messing with and including into the submit.php file
require('anticonfig.php');
// -----------------------------------------------------------------------------------
// You should not need to (and probably shouldn't) edit anything from here to the end.
// Determine how many words were entered
$cas_textcount = count( $cas_text );
// Copy the first element to a new last element
$cas_text[] = $cas_text[0];
// Set the first element to invalid
$cas_text[0] = "* * * INVALID * * *";
// Output the antispam image
// Pick a random font to use
$cas_font = $cas_fontpath . $cas_fontlist[ rand( 0, count( $cas_fontlist ) - 1 ) ];
// Set the default colors for when random text colors are not selected
if ( $cas_bgcolorset == "white") {
$cas_textcolor = array( 0, 0, 255 ); // blue text
$cas_bgcolor = array( 255, 255, 255); // white background
}
else {
$cas_textcolor = array( 255, 255, 255 ); // white text
$cas_bgcolor = array( 0, 0, 0); // black background
}
// If selected, pick a random color for the antispam word text
if( $cas_randomcolors )
{
$cas_rand = rand( 0, 4 );
switch( $cas_bgcolorset )
{
case "white":
$cas_textcolorchoice[0] = array ( 0, 0, 255 ); // blue
$cas_textcolorchoice[1] = array ( 0, 153, 0 ); // greenish
$cas_textcolorchoice[2] = array ( 204, 0, 0 ); // reddish
$cas_textcolorchoice[3] = array ( 203, 0, 154 ); // purplish
$cas_textcolorchoice[4] = array ( 0, 0, 0 ); // black
$cas_textcolor = $cas_textcolorchoice[$cas_rand];
break;
default:
$cas_textcolorchoice[0] = array ( 255, 255, 0 ); // yellow
$cas_textcolorchoice[1] = array ( 0, 255, 255 ); // blueish
$cas_textcolorchoice[2] = array ( 255, 153, 204 ); // pinkish
$cas_textcolorchoice[3] = array ( 102, 255, 102 ); // greenish
$cas_textcolorchoice[4] = array ( 255, 255, 255 ); // white
$cas_textcolor = $cas_textcolorchoice[$cas_rand];
break;
}
}
// Validate the input values
$cas_antiselect = intval( $_GET['antiselect'] );
if( $cas_antiselect < 1 || $cas_antiselect > $cas_textcount ) $cas_antiselect = 0;
$cas_antispam = $cas_text[ $cas_antiselect ];
// Start building the image
$cas_image = @imagecreate( $cas_imgwidth, $cas_imgheight )
or die("Cannot Initialize new GD image stream");
$cas_bgcolor = imagecolorallocate( $cas_image, $cas_bgcolor[0],
$cas_bgcolor[1], $cas_bgcolor[2] );
$cas_fontcolor = imagecolorallocate( $cas_image, $cas_textcolor[0],
$cas_textcolor[1], $cas_textcolor[2] );
// Check for freetype lib, if not found default to ugly built in
// capability using imagechar (Lee's mod)
// Also check that the chosen TrueType font is available
if( function_exists( 'imagettftext' ) && file_exists( $cas_font ) )
{
$cas_angle = 4; // Degrees to tilt the text
$cas_offset = 15; // Pixels to offset the text from the border
// Use png is available, since it produces clearer text images
if( function_exists( 'imagepng' ) && $cas_UsePngNotJpeg )
{
imagettftext( $cas_image, 28, $cas_angle, $cas_offset, $cas_imgheight - $cas_offset,
$cas_fontcolor, $cas_font, $cas_antispam );
header( "Content-type: image/png" );
imagepng( $cas_image );
} else {
imagettftext( $cas_image, 28, $cas_angle, $cas_offset, $cas_imgheight - $cas_offset,
$cas_fontcolor, $cas_font, $cas_antispam );
header( "Content-type: image/jpeg" );
imagejpeg( $cas_image );
}
} else {
$cas_fontsize = 5; // 1, 2, 3, 4 or 5 (higher numbers correspond to larger font sizes)
$tmp_len = strlen( $cas_antispam );
for( $tmp_count = 0; $tmp_count < $tmp_len; $tmp_count++ )
{
$tmp_xpos = $tmp_count * imagefontwidth( $cas_fontsize ) + 20;
$tmp_ypos = 10;
imagechar( $cas_image, $cas_fontsize, $tmp_xpos, $tmp_ypos, $cas_antispam, $cas_fontcolor );
$cas_antispam = substr( $cas_antispam, 1);
}
header("Content-Type: image/gif");
imagegif( $cas_image );
} // end if
imagedestroy( $cas_image );
?>
In submitForm.php I added the following to the form:
Then, finally, in submit.php I added the following bits to do the validation code:
//Added for antispam require_once($PHP_PATH_TO_ROOT.'antispam/anticonfig.php'); . . . //Added for antispam $cas_antiselect = intval($_POST['antiselect']); $cas_antiword = $_POST['antiword']; // Determine how many words were entered $cas_textcount = count( $cas_text ); // Copy the first element to a new last element $cas_text[] = $cas_text[0]; // Set the first element to invalid $cas_text[0] = "* * * INVALID * * *"; //Determine the correct word $cas_antispam = $cas_text[ $cas_antiselect ]; . . . //Added for antispam if ($cas_antispam != $cas_antiword) { write_error($cas_errorMessage); $error = true; }Once done, I set up a test site, and David started a thread in the forums asking people to submit some clearly marked test links. After a few minor bug fixes, we were happy and we set it live, and it’s been running for almost 16 hours now with with no complaints.
It was suggested in the forum that fetching the image with a ?antiselect=index on the query string would allow spammers to figure out the words and program the bots with them. That’s true but, I think, a little unrealistic. There are somewhat over a hundred and twenty words in the list, and that seems like an awful lot of work to spam up the MirthCanal, the only place where it would work. Also, David could quite easily change the order of the words. Hell, take the first word and put it last, and that’s the end of that. I could have saved the index in the session, but since Spaceboy wasn’t using sessions I didn’t want to start doing that. Still, I was thinking that the word index could be encoded using the date or something so that the numbers would change, at least, on a daily basis. I’ll do that if it becomes a problem.
In general, the above solution provides a fairly easy to implement, generic CAPTCHA component for PHP based sites. We’ve (E-Technik) developed a similar solution in .Net for the Good Fellas website, and it’s been successful there too. I suppose, at some stage, spam-bots will evolve to be able to read these images, and then we’ll start the fight again, and develop a new technology to stop this kind of thing. I still wonder what kind of results these spammers get from spam-botting sites. Surely there aren’t enough people that click on these links to make it worthwhile.
PS. Someone suggest a decent code display plugin for WordPress, please.
Return to: Anti-Spamming the MirthCanal
Social Web