Making a CAPTCHA-style spam catcher in laravel

One way to combat “bots” that automatically fill in web forms is by using the CAPTCHA
technique. This shows the user an image with some random letters; the user must fill in a text
field with those letters. In this recipe, we will create a CAPTCHA image and verify that the user
has entered it correctly.

Getting ready

We need a standard Laravel installation and make sure we have the GD2 library installed on
our server, so we can create an image.

How to do it…

To complete this recipe, follow these steps:
1. In our app directory, create a directory named libraries, and in our
composer.json file, update it as follows:
“autoload”: {
“classmap”: [
“app/commands”,
“app/controllers”,
“app/models”,
“app/database/migrations”,
“app/database/seeds”,
“app/tests/TestCase.php”,
“app/libraries”
]
},
2. In our app/libraries directory, create a file named Captcha.php to hold our
simple Captcha class:
<?php
class Captcha {
public function make()
{
$string = Str::random(6, ‘alpha’);
Session::put(‘my_captcha’, $string);
$width = 100;
$height = 25;
$image = imagecreatetruecolor($width,
$height);
$text_color = imagecolorallocate($image, 130, 130,
130);
$bg_color = imagecolorallocate($image, 190, 190,
190);
imagefilledrectangle($image, 0, 0, $width, $height,
$bg_color);

imagestring($image, 5, 16, 4, $string,
$text_color);
ob_start();
imagejpeg($image);
$jpg = ob_get_clean();
return “data:image/jpeg;base64,”
. base64_encode($jpg);
}
}

3. In the root of our app, open the command-line interface to update the
composer autoloader:
php composer.phar dump-autoload
4. Create a route in routes.php to hold the form with captcha:
Route::get(‘captcha’, function()
{
$captcha = new Captcha;
$cap = $captcha->make();
return View::make(‘captcha’)->with(‘cap’, $cap);
});
5. Create our captcha view in the app/views directory with the name captcha.php:
<h1>Laravel Captcha</h1>
<?php
if (Session::get(‘captcha_result’)) {
echo ‘<h2>’ . Session::get(‘captcha_result’) . ‘</h2>’;
}
?>
<?php echo Form::open() ?>
<?php echo Form::label(‘captcha’, ‘Type these letters:’) ?>
<br>
<img src=”<?php echo $cap ?>”>
<br>
<?php echo Form::text(‘captcha’) ?>
<br>
<?php echo Form::submit(‘Verify!’) ?>
<?php echo Form::close() ?>

6. Create a route to compare the captcha value and the user input:
Route::post(‘captcha’, function()
{
if (Session::get(‘my_captcha’) !==
Input::get(‘captcha’)) {
Session::flash(‘captcha_result’, ‘No Match.’);
} else {
Session::flash(‘captcha_result’, ‘They Match!’);
}
return Redirect::to(‘captcha’);
});

How it works…

We begin by updating our composer.json file to add our libraries directory to the
autoloader. Now, we can add any classes or libraries we’d like into that directory, even if
they’re custom classes or possibly some legacy code.
To keep things simple, we create a simple Captcha class with a single make() method. In
this method, we first create a random string using Laravel’s Str:random(), which we tell to
output a 6-character string of only letters. We then save that string to a session, so we can
use it for validation later.
Using the string, we create a 100×25 pixel jpg image, with a gray background and darker gray
text. Instead of saving the file to the server, we use the output buffer and save the image data
to a variable. That way, we can create a data URI to send back to our route.
Next, we need to run composer’s dump-autoload command, so our new class can be used
by the application.
In our captcha route, we use the Captcha class to create the captcha data URI and
send it to our form. For our purposes, the form will simply display the image and ask for the
characters in a text field.
When the user submits the form, we compare the Session that the Captcha class created
with the user input. In this recipe, we’re just checking if the two values match but we could
also create a custom validation method and add it our rules. We then set a session saying if
it matched or not, and return the user back to the CAPTCHA page.

Leave a Reply

Your email address will not be published. Required fields are marked *