Hoy vamos a crear un captcha MUY CASERO y básico en PHP con la ayuda de la libreria GD. Vamos a ir explicando paso a paso cada pedazo de código y al final de la lectura espero que todos se vayan con una sonrisa habiendo aprendido correctamente como se hace.
En varias entradas de mi blog ya hemos usado esta libreria GD. Pero hoy recurriremos de nuevo a ella de una manera muy fácil.
El objetivo sera crear un captcha “presentable”, Legible y de alguna manera “seguro”. Lo dividiremos en 8 etapas (8 captchas) para obtener un unico resultado.
Captcha 1
Lo primero que debemos hacer es crear nuestro recurso que es una imagen, para lo que utilizaremos una función llamada “imagecreatetruecolor” a la cual solo debemos definirle ancho y alto de la imagen deseada.
imagecreatetruecolor nos permite crear una imagen que maneje transparencias y ciertos tipos de fuentes de letra (Posteriormente se utilizaran)
imagecreate Bien podriamos usar esta función que es mas sencilla, pero que no nos da las funcionalidades de imagecreatetruecolor
1 2 3 | $ancho=100; $alto=55; $image=imagecreatetruecolor($ancho,$alto); |
Una vez creado el recurso vamos a pintar esa rectángulo con un color de fondo. Este color de fondo sera aleatorio. Para ello usaremos la función imagecolorallocate la cual define un color del modo RGB.
imagecolorallocate($recurso,$R,$G,$B); De esta manera definiremos los colores deseados.
Como se quiere que sea variable el color, usaremos la funcion rand() (Que de ahora en adelante es nuestra mejor amiga).
rand($MinVal,$MaxVal); A la función rand() se le define el rando de valores que puede tomar. En el caso de los colores RGB solo pueden ir de 0 al 255.
1 2 3 4 | $rgb[0] = rand(0,255); $rgb[1] = rand(0,255); $rgb[2] = rand(0,255); $RandomColor=imagecolorallocate($image,$rgb[0],$rgb[1],$rgb[2]); |
Ahora solo debemos pintar nuestra imagen. Para esta simple tarea usaremos la funcion imagefill() que rellena con un color
imagefill($recurso,$X,$Y,$color); Pinta la imagen a partir de una cordenada XY de un determinado color.
1 | imagefill($image,0,0,$RandomColor); |
Al final damos salida a la imagen para que se muestre en el navegador. Para eso usaremos las funciones header() imagepng() e imagedestroy()
header(“Content-type: image/png”); Le diremos al navegador como interpretar el recurso imagen/png
imagepng($recurso); La imagen sera una imagen PNG
imagedestroy($recurso); Destruimos del buffer la imagen
Nuestro codigo final de esta primera imagen es:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php //Crear $ancho=100; $alto=55; $image=imagecreatetruecolor($ancho,$alto); //Colores $rgb[0] = rand(0,255); $rgb[1] = rand(0,255); $rgb[2] = rand(0,255); $RandomColor=imagecolorallocate($image,$rgb[0],$rgb[1],$rgb[2]); //Fondo imagefill($image,0,0,$RandomColor); //Salida header("Content-type: image/png"); imagepng($image); imagedestroy($image); ?> |
Y resulta en la siguiente imagen:
Captcha 2
NOTA: De aquí en adelante los códigos se AGREGARAN a lo ya realizado.
Ahora vamos a crearle a nuestro captcha un marco de color negro. Para esta tarea usaremos la función imageline Y claro definiremos el color negro =)
imageline($recurso,$x1,$y1,$x2,$y2); Crea una linea definiendle el principio y final de la linea.
1 2 3 4 5 6 | $negro=imagecolorallocate($image,0,0,0); //Marco imageline($image,0,0,$ancho,0,$negro); imageline($image,0,0,0,$alto,$negro); imageline($image,$ancho-1,$alto-1,0,$alto-1,$negro); imageline($image,$ancho-1,$alto-1,$ancho-1,0,$negro); |
Aquí creamos nuestro marco con cuatro lineas.
Captcha 3
Ya que vimos como hacer lineas, metamos un poco de ruido al captcha. Para esto meteremos una simple rejilla con puras lineas. Y ahora agregaremos un tipo de color gris
1 2 3 4 5 6 7 8 | $gray=imagecolorallocate($image,100,100,100); //Rejilla imageline($image,25,0,25,$alto,$gray); imageline($image,50,0,50,$alto,$gray); imageline($image,75,0,75,$alto,$gray); imageline($image,0,13,$ancho,13,$gray); imageline($image,0,26,$ancho,26,$gray); imageline($image,0,39,$ancho,39,$gray); |
Captcha 4
Es hora de agregar una simple cadena de Texto al captcha. Ahora usaremos la función imagestring()
imagestring($recurso,$Tamaño,$X,$Y,$Cadena,$color); El tamaño va de 1 a 5, se define la posicion XY donde se inicia la cadena, posteriormente la cadena y por ultimo el color de la cadena
1 2 | //TextoSimple imagestring($image,2,1,41,"@hecky",$negro); |
Captcha 5
Ahora empieza lo divertido. Agregaremos otra cadena que sera el código variable del captcha. Para esto usaremos varias funciones rand(), md5(), strtoupper(), str_replace() y substr().
Explicare por puntos que haremos con cada función:
- Crear un numero Aleatorio del 9999 al 99999 con rand(9999,99999);
- Usaremos la función md5($recurso); para que el resultado del numero aleatorio nos regrese una cadena mas larga y que nos incluirá letras
- Ahora esa cadena la convertiremos a Mayúsculas con la función strtoupper($recurso);
- En el siguiente captcha usaremos una fuente de letra especial, y con este tipo de fuente no se distinguen bien los 0 y O, por lo que los remplazare con nada con la funcion str_replace($Acambiar,$Cambio,$Recurso);
- Al final no quiero la cadena de 32 caracteres que nos devuelve el md5, por lo que solo obtendre los 5 primeros caracteres con la función substr($recurso,$inicio,$final);
Nuestro código que nos devolverá una cadena variable sera:
1 2 | //TextoRandom $random=substr(str_replace("0","",str_replace("O","",strtoupper(md5(rand(9999,99999))))),0,5); |
Y ya al final solo pintamos la cadena el la imagen, de color negro y centrada;
1 | imagestring($image,5,30,17,$random,$negro); |
Captcha 6
VAMOS A DARLE COLOR A ESA CADENA VARIABLE!!! En el anterior código pintamos la cadena de color negro, pero ese color estático se puede perder cuando nuestro fondo aleatorio tome un color obscuro. Por ello ahora idearemos una forma de que siempre contrasten nuestros colores.
Para esta tarea lo que haremos sera tomar el valor aleatorio del fondo y se lo restaremos a 255 (que es el valor maximo que puede llegar a tomar).
Ej:
Si nuestro fondo es 255,255,255 -> Blanco haremos esto:
255-255,255-255,255-255 -> 0,0,0 (Negro) Aqui nuestro fondo sera blanco y nuestro texto negro.
Por el contrario si:
Nuestro fondo es 0,0,0 -> Negro:
255-0,255-0,255-0 -> 255,255,255 (Blanco) Fondo Negro, letras blancas.
Se esta no es la mejor solución y mas aún cuando los valores se acerquen a la mitad de 255, pero para estos fines si nos ayudara.
Nustro nuevo color lo definiremos entonces de la siguiente manera:
1 | $RandomColorInverted=imagecolorallocate($image,255-$rgb[0],255-$rgb[1],255-$rgb[2]); |
Y claro cambiaremos al pintar la cadena de $negro a $RandomColorInverted
1 | imagestring($image,5,30,17,$random,$RandomColorInverted); |
Captcha 7
Ahora hagamos un poco mas agradable a la vista el captcha. Vamos a modificar el tipo de letra predeterminado de PHP y GD y usemos una fuente personalizada. En mi caso elegi la fuente gunplay.ttf y para hacer uso de ella ya no usaremos imagestring(), ahora usaremos imagefttext();
Para poder invocarla necesitaremos algunas cosas:
- Que nuestro servidor tenga las librerias de freetype
- Que nuestra imagen sea del tipo “truecolor“
- La fuente .ttf que se desea utilizar.
Y la sintaxis es:
imagefttext($recurso,$tamaño,$angulo,$x,$y,$color,$fuente,$cadena); Lo que cambia aquí es que el Tamaño ya no esta limitado a 5. El ángulo nos permite darle un giro a nuestra letra y la $fuente es la dirección de nuestro ttf
1 2 3 | //TextFont $ttf = "./gunplay.ttf"; imagefttext($image,22,rand(-10,15),12,37,$RandomColorInverted,$ttf,$random); |
Nuestra letra tendra un tamaño de 22, en $ttf definiremos la dirección y nombre de la fuente a usar y en el angulo, lo haremos variable, por lo que le metimos un rand(-10,15), para que gire de -10º a 15º, y de esta manera evitamos un texto completamente estático.
Captcha 8
ULTIMO PASO!! Ya que tenemos esto casi terminado, nos falta meter un poco de dificultad para la lectura, para ello agregaremos otro nivel de ruido.
Ahora en vez de lineas meteremos puntos, que sera pixeles aleatorios. Para esta tarea se usa la funcion imagesetpixel();
imagesetpixel($recurso,$x,$y,$color); Solo se le debe indicar la posición del pixel y el color
Como verán es demasiado sencillo. En nuestro caso vamos a meterle 700 pixeles en posiciones aleatorias, con el color que contrarresta. Para eso usaremos un simple bucle para que nos meta de un golpe los 700 pixeles.
1 2 3 4 5 6 | //Ruido for ($i=0;$i $randx=rand(0,100); $randx=rand(0,100); $randy=rand(0,55); imagesetpixel($image,$randx,$randy,$RandomColorInverted); } |
En la parte de arriba claramente se ve como cada vez que se accede al ciclo, se toman nuevos valores aleatorios para posicionarlos y así dar el efecto de ruido.
Por ultimo Ya no queremos ese texto feo de “@hecky” asi que BORRAREMOS ESTA LINEA:
1 2 | //TextoSimple imagestring($image,2,1,41,"@hecky",$negro); |
Y ya quedo!!!
CODIGO FINAL
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 | <?php $ancho=100; $alto=55; //Colores $image=imagecreatetruecolor($ancho,$alto); $negro=imagecolorallocate($image,0,0,0); $gray=imagecolorallocate($image,100,100,100); $rgb[0] = rand(0,255); $rgb[1] = rand(0,255); $rgb[2] = rand(0,255); $RandomColor=imagecolorallocate($image,$rgb[0],$rgb[1],$rgb[2]); $RandomColorInverted=imagecolorallocate($image,255-$rgb[0],255-$rgb[1],255-$rgb[2]); //Fondo imagefill($image,0,0,$RandomColor); //Marco imageline($image,0,0,$ancho,0,$negro); imageline($image,0,0,0,$alto,$negro); imageline($image,$ancho-1,$alto-1,0,$alto-1,$negro); imageline($image,$ancho-1,$alto-1,$ancho-1,0,$negro); //Rejilla imageline($image,25,0,25,$alto,$gray); imageline($image,50,0,50,$alto,$gray); imageline($image,75,0,75,$alto,$gray); imageline($image,0,13,$ancho,13,$gray); imageline($image,0,26,$ancho,26,$gray); imageline($image,0,39,$ancho,39,$gray); //TextoRandom $random=substr(str_replace("0","",str_replace("O","",strtoupper(md5(rand(9999,99999))))),0,5); //TextFont $ttf = "./gunplay.ttf"; imagefttext($image,22,rand(-10,15),12,37,$RandomColorInverted,$ttf,$random); //Ruido for ($i=0;$i<=700;$i++){ $randx=rand(0,100); $randy=rand(0,55); imagesetpixel($image,$randx,$randy,$RandomColorInverted);} //Salida header("Content-type: image/png"); imagepng($image); imagedestroy($image); ?> |
Captcha Final
Bonito ¿verdad? Se que no es el gran catpcha, pero mínimo es agradable a la vista, en tres pruebas de ataque de OCR resistió (después veremos esto), esta mejor que muchos captchas que ofrecen por ahí, y lo mas importante de todo es que LO HICIMOS NOSOTROS MISMOS.
Después de explicar todo esto, no creo quede alguna duda, pero en todo caso pueden preguntar. Hoy tuvimos un gran acercamiento al manejo de Imágenes con PHP y GD y creamos nuestro primer captcha casero. Lo demás ya solo es cosa de obtener el parámetro y validar sesión (Pero eso ya se los dejo a ustedes).
Sin mas espero les agradara el articulo.
Saludos
hecky@neobits.org
Sigueme en Twitter: http://twitter.com/hecky








Pingback: Nueva versión de Flu b0.5.2