Beta 42

Research and Development

Menu

Anti-bot Using php

If your website is being overwhelmed with spam, automatic registrations, automatic forum or classified ad submissions etc., you can solve this problem by introducing a picture in your HTML forms that only a human can recognize.

A simple anti-bot registration check that requires registering users to type a random text drawn on the picture. Otherwise the registration will not proceed.

If every forum employs completely different anti-bot measures it makes it almost impossible to create bots for mass-automated posting.

One approach can be to create a set of images with predefined answers and randomly choose one on every page refresh. Images can also be created dynamically, on the fly.

Dynamic Image Creation

The easiest way to understand how to create an image is by looking at some sample code:

$my_img = imagecreate( 200, 80 );
$background = imagecolorallocate( $my_img, 0, 0, 255 );
$text_colour = imagecolorallocate( $my_img, 255, 255, 0 );
$line_colour = imagecolorallocate( $my_img, 128, 255, 0 );
imagestring( $my_img, 4, 30, 25, "S4MPL3", $text_colour );
imagesetthickness ( $my_img, 5 );
imageline( $my_img, 30, 45, 165, 45, $line_colour );

header( "Content-type: image/png" );
imagepng( $my_img );
imagecolordeallocate( $line_color );
imagecolordeallocate( $text_color );
imagecolordeallocate( $background );
imagedestroy( $my_img );

The above code creates a 200×80 PNG image with a blue background and yellow text. It can be called from within your web page simply by referencing the php file. For example, if the PHP file that contains the above code is called myimage.php, then the HTML code to invoke it can simply be:

<img src="antibotimpage.php" alt="Image created by a PHP script" width="200" height="80">

Creating the Image

The first thing the code does is to call the imagecreate() function with the dimensions of the image, namely its width and height in that order. This function returns a resource identifier for the image which we save in $my_img. The identifier is needed for all our operations on the image.

If the function fails for some reason, it will return FALSE. If you want your code to be robust, you should test for this.

Using Colours in PHP

Before you can use any sort of colours in your image at all, you will need to allocate the colour. Colours are represented by three digits, known as the RGB value. The first digit denotes the red component, the second the green and the third blue, hence RGB, for Red-Green-Blue. These are the same colour values that you use for your web page as well as numerous other computer applications.

Colours are allocated using the imagecolorallocate() function. This function will automatically fill the background of the image with the colour the first time you call it, as well as return an identifier for that particular colour. Subsequent calls to imagecolorallocate() will simply create a colour identifier for your colour, without affecting your image background.

As you can see from the above code, the script allocates a blue identifier for the image, and in so doing, causes imagecolorallocate() to set the background to blue automatically. It also allocates a colour identifier for yellow and one for a shade of green. The latter two identifiers will be used later to write text and draw a line.

imagecolorallocate() returns FALSE if the function fails for any reason.

Drawing a Line and Setting the Thickness of the Brush

The imageline() function can be used to draw a line to the image. To set the thickness of the brush used to draw the line, you may want to call the imagesetthickness() function. The numeric parameter to imagesetthickness() is the thickness of the brush in pixels. The default value is 1.

The imageline() function is called with the start and end coordinates of the line, in x, y format. Since $line_colour was set to a shade of green earlier, the line will be green in the example.

Output the Image

Since the output of the script is the image itself, an "image/png" content type header is being sent to the browser telling it that what follows are the bytes of a PNG image. The function imagepng() is then called to generate the necessary image from the $my_img image identifier. Since imagepng() was called without a second parameter, the function automatically sends its output to the browser. If you prefer to save your image, don’t call the header() function to output the header, and call imagepng() with the filename of the image for its second parameter, like the following:

imagepng( $my_img, "my_new_image.png" );

Your image does not have to be a PNG image. You can use imagegif() or imagejpeg() to create GIF or JPG images respectively. You should of course send the correct content type header for the type of image you are creating. A jpeg image should have a content type of "image/jpeg" while a gif image "image/gif". Note though that GIF support may or may not necessarily be compiled into the version of the GD library your web host is using, so if you’re not sure, use one of the other file formats.

Freeing Resources

On completion, the program releases the resources associated with the image by calling imagecolordeallocate() and imagedestroy(). I'm not sure if any of these calls are really necessary if your script is going to terminate immediately, since I don't know if PHP automatically cleans up all these resources on the termination of the script.

Modifying an Existing Image

In most cases, creating an image from scratch is overkill. For most web purposes, you can usually design the basic background of your image using a normal image editor like Photoshop and only add any additional text or graphical elements that need to be dynamically drawn using PHP. This allows you to speed up your scripts and reduce the resource consumption on your web server. It also lets you create your picture using professional picture designing tools.

To use an existing GIF, JPEG or PNG image as a canvas on which you add additional elements, use one of the following functions instead of imagecreate():

imagecreatefromgif ( string $filename )
imagecreatefromjpeg ( string $filename )
imagecreatefrompng ( string $filename )

For example, if you created a GIF file called mytemplate.gif, the function can be called as follows:

$myimage = imagecreatefromgif ( "mytemplate.gif" );

Like the basic imagecreate() function, these functions return FALSE if they fail to load the image for any reason.

Using TrueType Fonts

If you want to use a True Type font, you will need to use imagettftext() instead. For details on how to use this function, please consult the function's manual page on php.net.

You should note a few things, though, before using this function:

Drawing to Your Image

Besides the line drawing function used above, PHP has other functions that you can use. A complete list of functions, along with their descriptions, can be found on www.php.net/gd. Functions include those that allow you to draw ellipses, arcs and polygons, change the style of your lines (to say dashed lines), and so on.

However, unless you have special reasons why you might want to dynamically draw complex pictures onto an image, you should consider creating your base image using a normal picture editor, load that image using a function like imagecreatefrompng(), and then only modifying small details with your script. Generating everything from scratch is unnecessary for most purposes, and can drain your web server resources.

Create Anti-bot image

Goal is to create script antibot_picture.php that outputs image similar to this:

First thing to do is define parameters:

$img_text = $_GET['id'];
$img_width = 200;
$img_height = 80;
$line_step = 12;
$fonts_dir = './antibot_fonts/';

Creating the image

As destribed above, to create the image you can use:

// create image
$my_img = imagecreate( $img_width, $img_height );

Image background

In order our text not to be recognisable by bots, we can add set of random factors to our image. The first one is backgroud color. We can define a set of convenient colors and randomly choose one upon every image creation:

function getbgcolor()
{
    $tmp_bg_colors = array("#ced1df", "#e5daf9", "#e1d7fb", "#d8d6fc", "#d1cfeb", "#dce7f1", "#d5eced", "#eef2f7", 
        "#e8ead0", "#e7e5f3", "#d4d7f9", "#dbefef");

    return $tmp_bg_colors[rand(0, sizeof($tmp_bg_colors) - 1)];
}

In order to pass that value to imagecolorallocate(), we must transform hex value to RGB values:

function html2rgb($color)
{
    if ($color[0] == '#')
        $color = substr($color, 1);

    if (strlen($color) == 6)
        list($r, $g, $b) = array($color[0].$color[1], $color[2].$color[3], $color[4].$color[5]);
    elseif (strlen($color) == 3)
        list($r, $g, $b) = array($color[0].$color[0], $color[1].$color[1], $color[2].$color[2]);
    else
        return false;

    $r = hexdec($r);
    $g = hexdec($g);
    $b = hexdec($b);

    return array($r, $g, $b);
}

Now we can apply this semi-random background color to our image:

// create background
$bgcolor = html2rgb(getbgcolor());
$background = imagecolorallocate( $my_img, $bgcolor[0], $bgcolor[1], $bgcolor[2] );

Draw lines

Lines color is dynamically chosen from the predefined set:

function getlinecolor()
{
    $tmp_line_colors = array("#b7d4a0", "#aca27f", "#978d5e", "#7a8f42", "#b6d578", "#979652", "#7a8549", 
        "#717753", "#8a783a", "#796626", "#796c3f", "#8c772f", "#af9333", "#94791a", "#805c35", "#a48d74");

    return $tmp_line_colors[rand(0, sizeof($tmp_line_colors) - 1)];
}

Distance between lines is defined by $line_step parameter, but with random number added on each step. Thickness of each line is also dynamically defined:

// draw lines
$linecolor = html2rgb(getlinecolor());
$line_colour = imagecolorallocate( $my_img, $linecolor[0], $linecolor[1], $linecolor[2] );
for ( $cury = $line_step + rand(0, 3); $cury < $img_height; $cury += $line_step + rand(0, 3) )
{
    imagesetthickness ( $my_img, rand(1, 2) );
    imageline( $my_img, 0, $cury, $img_width, $cury, $line_colour );
}
for ( $curx = $line_step + rand(0, 3); $curx < $img_width; $curx += $line_step + rand(0, 3) )
{
    imagesetthickness ( $my_img, rand(1, 2) );
    imageline( $my_img, $curx, 0, $curx, $img_height, $line_colour );
}

Draw text

First we choose text color:

function gettextcolor()
{
    $tmp_text_colors = array("#583a6f", "#453353", "#461658", "#273b1a", "#562418", "#6b2c41", "#4d5214", 
        "#28502e", "#172a73", "#112b8a", "#4c4f59", "#2f5144", "#2d4e54", "#284d6f", "#64355c" );

    return $tmp_text_colors[rand(0, sizeof($tmp_text_colors) - 1)];
}

Choice of gdf font and text position is also random:

// create text
$textcolor = html2rgb(gettextcolor());
$text_colour = imagecolorallocate( $my_img, $textcolor[0], $textcolor[1], $textcolor[2] );
$fonts = scandir($fonts_dir);
$fonts_max = count($fonts) - 2;
$font = imageloadfont($fonts_dir.$fonts[rand(2, $fonts_max)]);
$font_hei = imagefontheight($font);
$font_wid = imagefontwidth($font);
imagestring( $my_img, $font, rand(0, $img_width - strlen($img_text) * $font_wid), rand(0, $img_height - $font_hei), 
    $img_text, $text_colour );

Finish

To complete the script, we must send header info:

header( "Content-type: image/gif" );
imagegif( $antibot_img );
imagecolordeallocate( $line_color );
imagecolordeallocate( $text_color );
imagecolordeallocate( $background );
imagedestroy( $antibot_img );

Form integration

Dynamically generated image can be included to your web form like any other image:

<img border="0" src="anti-bot/image.php?id=<?php echo $out_string; ?>"  />

Variable $out_string contains text to be drawn and is set by the following function:

function build_random_string($string_size = 6, $include_numbers = true, $include_capital_letters = true, 
    $include_small_letters = true)
{
    $ret_str = "";
    $avalues = array();
    if($include_numbers)
    {
        foreach(range(49, 57) as $val)
            array_push($avalues, $val);
    }
    if($include_capital_letters)
    {
        foreach(range(65, 90) as $val)
            array_push($avalues, $val);
    }
    if($include_small_letters)
    {
        foreach(range(97, 122) as $val)
            array_push($avalues, $val);
    }

    if(count($avalues) > 0)
    {
        for( $i = 0; $i < $string_size; $i++)
            $ret_str .= chr($avalues[rand(0, count($avalues) - 1)]);
    }

    return $ret_str;
}

$out_string = build_random_string(6, true, true, false);

For easier (human) recognition, text of six characters is chosen, containing numbers and capital leters.

User input can be compared with $out_string variable, determining access to web form posting.

Conclusion

This is my approach to prevent mass-posting by bots. So far it has been 100% successful. You are welcome to test it and post your comments. All suggestions and improvements are welcome.