Un sistema de captcha (Completely Automated Public Turing test to tell Computers and Humans Apart) es la solución frecuente para evitar que robots spamers que visiten nuestro sitio puedan registrarse, enviar comentarios, participar en encuestas o cualquier yuyo en cuestión. Si bien esto no nos garantiza un 100% de efectividad , de seguro nos vale.

Supongamos un sitio web desarrollado en php al cual queremos implementarle una solución para frenar bots spamers, bien entonces instalamos re-captcha y listo, gracias por participar…

Ok, si bien nos sirve, quizás queramos saber cómo funciona y por ahí crear el nuestro propio, así que de eso va este post… comencemos.

Para generar imágenes en php nos valemos de la librería GD el cual está presente en la mayoría de los web hosting. En caso de utilizar localhost, instalando wamp para Windows ya nos viene incluido, y desde Linux bastaría con compilar php con la opción de gd, así como las librerías de libpng y libjpeg ( ./configure –with-gd=/… –with-png-dir=/… –with-jpeg-dir=… ).


Básicamente lo que necesitamos es generar una cadena de texto con letras y números al azar, almacenarlo en la sesión y luego comprobar con lo que el usuario inserto en la caja del formulario, en caso de coincidir se salto el captcha, caso contrario regresamos con un mensaje de error y con una nueva imagen para intentar.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<?php
/** Archivo captcha.php **/

//iniciamos session
session_start();

//  cantidad de letras en nuestro captcha
$passLength = 4;

//  tamaño para la caja de captcha
$width = 200;
$height = 80;
//  tamaño para el texto en pixceles
$textSize=30;
//  archivo ttf que vamos a utilizar
//  para escribir el texto en cuestion.
$fontFile ="my_special_font.ttf";


/**
 * Genera un string con la cantidad de caracteres pasado.
 * @param   int $size   cantidad de caracteres.
 * @return  $string     mayusculas y numeros
 */

function randomCH($size){
    $i=0;
    $keyString='';
    while ($i < $size) {
        // AZ (65-90) 0-9 (48-57)
        $str = rand(48, 90);
        if($str<65 && $str>57){
            continue;
        }else{
        $keyString .= chr($str);
        $i++;
        }
    }
    return $keyString;
}
//  almacenamos un strign aleatorio
$cacheString=randomCH($passLength);

//  almacenamos en la session la frase de la imagen
$_SESSION['cache_string']=$cacheString;

//  creamos una imagen utilizando las medidas setteadas
$imgCaptcha = imagecreatetruecolor($width, $height);

//  definimos algunos colores
//(esto debe hacerse luego de generar la imagen ya que debemos pasarle el recurso).
$blue = imagecolorallocate($imgCaptcha, 0, 0, 80);
$black = imagecolorallocate($imgCaptcha, 0, 0, 0);
$white = imagecolorallocate($imgCaptcha, 255,255,255);
$grey = imagecolorallocate($imgCaptcha, 100, 100, 100);  

// creamos un par de rectangulos para simular un borde azul
imagefilledrectangle($imgCaptcha, 10, 10, $width-10, $height-10, $blue);
imagefilledrectangle($imgCaptcha, 20, 20, $width-20, $height-20, $black);

// cada texto se inclinara algunos pixceles de forma aleatoria
$rotationLetter = rand(-10, 10);

// escribimos en las canvas utilizando como parametros nuestra imagen, el tamaño de texto
// la rotacion aleatoria, posicion x, posicion y, color, archivo ttf y por ultimo el string
// utilizamos 2 textos para simular una sombra en gris por encima de las letras blancas
imagettftext($imgCaptcha, $textSize, $rotationLetter, 50, 55, $grey, $fontFile, $cacheString);
imagettftext($imgCaptcha, $textSize, $rotationLetter, 52, 57, $white, $fontFile, $cacheString);


//  enviar cabezeras (para que no se cachee la imagen y el navegador siempre pida una nueva)
header("Expires: Tue, 20 Jul 2001 17:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");

//  enviar cabezera de imagen
header("Content-Type: image/png");

// utilizando imagejpeg o imagepng enviamos la imagen generada dinamicamente.
//imagejpeg($imgCaptcha);
imagepng($imgCaptcha);

// luego liberamos recursos en el sistema.
imagedestroy($imgCaptcha);
?>

Bien, leyendo los comentarios en el código se puede ir siguiendo paso a paso.
Ahora solo nos resta crear un formulario y llamar a la imagen como lo haríamos con una imagen estática desde HTML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
/** Archivo form.php **/
// iniciamos session
session_start();

// comprobamos si se envio el formulario y si lo que se envio coincide con el string guardado en session
if (isset($_POST["captcha"])) {
    if ($_REQUEST["captcha"] == $_SESSION['cache_string']) {
        echo "los caracteres ingresados son correctos..";
    } else {
        echo "error al ingresar los caracteres.";
    }
    exit(0);
}
?>
<p><img src="captcha.php"></p>
<p><form action="form.php" method="POST">
<input type="text" name="captcha" size=5 />
<input type="submit" value="enviar" />
</form></p>

Bien, con eso ya estaríamos, y bastaría adaptarlo a nuestras necesidades.
Como comentarios finales, hay que prestar atención a algún par de cosirijillas, como por ejemplo:
1. debe iniciarse session en ambos archivos, (si el cliente no acepta cookies no nos servirá).

2. las cabeceras para evitar que la imagen sea cacheada por el navegador, y las cabeceras para decirle al navegador que es una imagen lo que debe esperar deben ser enviadas antes que nada, es decir que no nos vale generar ningún tipo de salida y luego intentar enviar cabeceras, esto produciría un error.

3. hay que revisar las rutas, pueden utilizar __FILE__ para referirse al path absoluto donde se encuentra en el servidor y luego concatenar rutas para encontrar el archivo de fuente, imágenes que quieran utilizar para complicar un poco más el texto, etc. En este caso tenemos todos los archivos en la misma carpeta (captcha.php, form.php y font.ttf).

4. al final del código utilicé la función imagepng($imagen), y también valdría utilizar imagejpeg($imagen), con una diferencia en calidad, sobre todo si utilizan imágenes externas con degrado, y por supuesto una saca en pantalla una imagen png, mientras que la otra una jpg.

5. el tipo de fuente es Bauhaus (la pueden buscar en su directorio de fuentes) o utilizar cualquier otra, claro está que deberían de chechear que sea legible por humanos.

Resultado final:
resultado de captcha

Para mas información sobre como manipular imágenes en php, pueden consultar el siguiente enlace:

http://php.net/manual/en/book.image.php