Making a CAPTCHA-style spam catcher
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:
- In our
app
directory, create a directory namedlibraries
, and in ourcomposer.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" ] },
- In our
app/libraries
directory, create a file namedCaptcha.php
to hold our simpleCaptcha
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); } }
- In the root of our app, open the command-line interface to update the
composer
autoloader:php composer.phar dump-autoload
- Create a route in
routes.php
to hold the form withcaptcha
:Route::get('captcha', function() { $captcha = new Captcha; $cap = $captcha->make(); return View::make('captcha')->with('cap', $cap); });
- Create our
captcha
view in theapp/views
directory with the namecaptcha.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() ?>
- 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 100x25 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.