CAPTCHA images are used to prevent automated form submission. In this post I am going to demonstrate how you can add an extra layer to your HTML forms using custom CAPTCHA images to prevent automated form submission.
So we will first create a php class captcha.php, which will contain the methods to create a captcha image, add random text to image, add noise to image and blur captcha image. image-captcha.php will be used to use methods from captcha class and will this file be used a source attribute for html image element. index.php will hold the html form and style.css will apply styling to form.
captcha.php
<?php
class captcha{
private $image;
private $color;
public $width;
public $height;
function __construct($width = 120,$height = 60,$background = "#4F5B93",$color = "#ffffff"){
// Create captcha image, Set height & width, text color when object is initialized
$this->image = imagecreatetruecolor($width, $height);
$this->width = intval($width);
$this->height = intval($height);
$color = $this->decimal_color($color); // Get decimal color value from hex color value
$this->color = $this->true_color($color); // Set true color from decimal color
$background = $this->decimal_color($background);
$background = $this->true_color($background);
// Fill background color to image
imagefill($this->image, 0, 0, $background);
}
/*****************************************************************
| Function to add noise to captcha image
** @param : (hex) Noise color
******************************************************************/
public function captcha_noise($color = "#8892BF"){
$font_color = $this->decimal_color($color);
$font_color = $this->true_color($font_color);
// Loop x co-ordinates of image
for($r = 0; $r < $this->width; $r++) {
// Loop y co-ordinates of image
for($c = 0; $c < $this->height; $c++) {
// Add noise on step of 1 pixel, skipping one pixel and noise to next pixel
if (mt_rand(0,1) == 1)
imagesetpixel($this->image, $r, $c, $font_color);
}
}
}
/*****************************************************************
| Function to blur captcha image
* @param : (int) Number of times image should be blurred
******************************************************************/
public function captcha_blur($count = 3){
$count = intval($count) > 10 ? 10 : $count;
for($i = 1; $i <= $count; $i++){
imagefilter($this->image, IMG_FILTER_GAUSSIAN_BLUR);
}
}
/*****************************************************************
| Function to return created captcha image
******************************************************************/
public function captcha_image(){
$this->captcha_text();
ob_start();
header("Content-Type: image/jpeg");
imagejpeg($this->image,NULL,100);
$captcha = ob_get_clean();
imagedestroy($this->image);
return $captcha;
}
/*****************************************************************
| Function to blur captcha image
* @param : (hex) Hexadecimal color value
******************************************************************/
private function decimal_color($color){
$color = strpos($color, "#") !== FALSE ? substr($color, 1) : $color;
if(strlen($color) === 6){
$red = substr($color, 0,2);
$green = substr($color, 2,2);
$blue = substr($color, 4,2);
}else{
$red = substr($color, 0,1).substr($color, 0,1);
$green = substr($color, 1,1).substr($color, 1,1);
$blue = substr($color, 2,1).substr($color, 1,1);
}
$red = hexdec($red);
$green = hexdec($green);
$blue = hexdec($blue);
return ["red" => $red, "green" => $green, "blue" => $blue];
}
/*****************************************************************
| Function to convert decimal color to true color
* @param : (array) Array of decimal color value for red,green,blue
******************************************************************/
private function true_color($color){
return imagecolorallocate($this->image, $color["red"], $color["green"], $color["blue"]);
}
/*****************************************************************
| Function to generate random string for captcha
* @param : (int)Length of characters
******************************************************************/
private function random_string($length){
$characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$random_string = "";
for($i = 0; $i < $length; $i++){
$random_string .= $characters[mt_rand(0,strlen($characters) - 1)];
}
return $random_string;
}
/*****************************************************************
| Function to add random string to captcha image
******************************************************************/
private function captcha_text(){
$font_file = realpath(dirname(__FILE__)."/sketch_block.ttf");
$font_size = $this->height * 0.6;
$font_color = $this->color;
$random_string = $this->random_string(5);
$ttf_box = imagettfbbox($font_size, 0, $font_file, $random_string);
$xr = abs( max( $ttf_box[2], $ttf_box[4] ) );
$xy = abs( max( $ttf_box[5], $ttf_box[7] ) );
$x = ( $this->width - $xr ) / 2;
$y = ( $this->height + $xy ) / 2;
imagettftext($this->image, $font_size, 0, $x, $y, $font_color, $font_file, $random_string);
$_SESSION["captcha"] = $random_string;
}
}
?>
captcha-image.php
<?php
session_start();
include_once("captcha.php");
$captcha = new captcha(280,60);
$captcha->captcha_noise(); // Add noise to captcha image
$captcha->captcha_blur(); // Blur captcha image
echo $captcha->captcha_image();
?>
index.php
<?php
session_start();
if(!empty($_POST)){
if($_POST['captcha'] === $_SESSION["captcha"]){
$success = 1;
}else{
$success = 0;
}
$_SESSION["captcha_status"] = $success;
header("Location: ".$_SERVER["REQUEST_URI"]);
exit();
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Generate Captcha Image in PHP - Demo</title>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type'/>
<link rel="stylesheet" href="css/style.css" />
<script type="text/javascript">
function refresh_captcha(){
var random = Math.random();
document.querySelector(".captcha-image").src = "captcha-image.php?" + random;
}
</script>
</head>
<body>
<div class="main-container">
<div class="section">
<div class="captcha-box">
<?php
if(isset($_SESSION["captcha_status"])){
$success = $_SESSION["captcha_status"];
if($success){?>
<div class="captcha-message">Captcha verified</div>
<?php
}else{?>
<div class="captcha-message error">Captcha verification failed</div>
<?php
}
unset($_SESSION["captcha_status"]);
}?>
<form name="captcha_form" method="POST">
<img class="captcha-image" src="captcha-image.php" />
<div><input type="text" name="captcha" class="form-control" /></div>
<p>Can't read? Click <a href="javascript:refresh_captcha();">here</a> to refresh</p>
<button type="submit" class="btn btn-green btn-block">Verify</button>
</form>
</div>
</div>
</div>
</body>
</html>
style.css
*{
box-sizing: border-box;
}
html,body{
margin: 0px;
padding: 0px;
}
body{
background: #f0f0f0;
font: normal normal 14px Open Sans,Verdana, Arial;
}
.main-container{
max-width: 1024px;
margin: 0px auto;
}
.section {
padding: 15px;
}
.form-control {
padding: 10px;
border: 1px solid #ddd;
width: 100%;
margin-bottom: 5px;
color: #444;
}
.btn-green{
display: inline-block;
padding: 5px 10px;
cursor: pointer;
background: #00a65a;
border: 1px solid #009549;
color: #fff;
width: 100%;
}
.captcha-box{
max-width: 300px;
background: #ddd;
padding: 10px;
}
.captcha-message{
background: #fff;
padding: 5px;
text-align: center;
color: #009549;
border: 1px solid #009549;
margin-bottom: 10px;
}
.captcha-message.error{
color: #DD1A16;
border: 1px solid #DD1A16;
}