Errores PHP
X
Usuario
Password
0 FPS

Captchas (3 Captcha arrastrar y soltar PHP + JQuery)

05 de Diciembre del 2011 por Josep Antoni Bover, 8648 visitas, 6 comentarios, 12 votos con una media de 4.17 sobre 5.
Categorías : PHP, JQuery, Programación, HTML.
La web está en segundo plano, animación en pausa.
Cargando animación...
Captchas (3 Captcha arrastrar y soltar PHP + JQuery)

En los anteriores tutoriales vimos un tipo de captcha muy básico que requería introducir el resultado de una suma en una caja de texto, pero esta vez vamos a enfocar el tema de otra forma. Se mostraran nueve resultados en recuadros independientes, y debajo una suma, y lo que habrá que hacer es arrastrar el resultado correcto para validar el captcha.

Para hacer esto vamos a utilizar PHP y JQuery. También necesitaremos la librería JQuery UI que es la que nos permite hacer el efecto arrastrar y soltar compatible para todos los navegadores.

Aunque este ejemplo sigue basándose en una suma como los anteriores, es bastante más original ya que el usuario no tiene que escribir nada, sino que tiene que arrastrar el resultado correcto hacia la zona de los interrogantes grises.

En este caso el servidor ya no se guarda el resultado de la operación, si no que se guarda la ID de la imagen con el resultado correcto.

Estructuración

Este ejemplo va a estar dividido en varios archivos :

Captcha3_Ejemplo.php : Este archivo contiene el código HTML y contendrá enlaces a los estilos CSS, código JavaScript, y código PHP para el captcha.
Captcha3_Ejemplo.js : Este archivo contiene todo el código javascript para el captcha.
Captcha3_Ejemplo.css : Este archivo contiene los estilos css para el captcha.
Captcha3_Ejemplo_Motor.php : Este archivo contiene las funciones necesarias para crear el captcha, generar sus imágenes y validarlo.

El archivo "Captcha3_Ejemplo_Motor.php" funciona de dos formas, ya que puede ser incluido en otro archivo php para llamar a sus funciones y también puede ser utilizando independientemente con parámetros para realizar una función en concreto.

Archivo Captcha3_Ejemplo_Motor.php posibles parámetros
1
2
3
4
5
6
7
session_start();
// Miramos si hay que generar una imagen
if (isset($_GET["Img"]))
if ($_GET["Img"] != "") GenerarImagen($_GET["CaptchaID"], $_GET["Img"]);
// Miramos si hay que validar el resultado
if (isset($_GET["ResultadoID"]))
if ($_GET["ResultadoID"] != "") Validar($_GET["ResultadoID"]);

En la línea 3 comprobamos si se ha pasado el parámetro "Img" en cuyo caso indicaría que debe generar una imagen para uno de los valores aleatorios.

En la línea 5 comprobamos si se ha pasado el parámetro "ResultadoID" para proceder a validar el resultado.

Voy a omitir la función "GenerarImagen" ya que en esencia es muy simple, y si habéis visto el anterior tutorial sobre captchas Captchas (2 Captcha básico con php) no vais a tener ningún problema en ver cómo funciona.

Creación del captcha

La función "Validar" es de lo más simple, pero mejor la dejamos para más tarde ya que para comprenderla mejor tenéis que ver como se crea el captcha :

Archivo Captcha3_Ejemplo_Motor.php función CrearCaptcha
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
// Función que genera los valores aleatorios y escribe el código HTML del captcha
function CrearCaptcha() {
// Creamos las variables necesarias dentro del $_SESSION
if (!isset($_SESSION["CaptchaID"])) {
$_SESSION["CaptchaID"] = 0;
$_SESSION["Captcha"] = array();
}
$CaptchaID = ++$_SESSION["CaptchaID"];
$_SESSION["Captcha"][$CaptchaID] = array();
// Creamos la operacion aleatoria
$Valor1 = rand(10, 49);
$Valor2 = rand(10, 49);
$Resultado = $Valor1 + $Valor2;
// Creamos 8 valores aleatorios
$Valores = array();
for ($i = 0; $i < 8; $i++) {
do {
$Valores[$i] = rand(10, 99);
} while ($Valores[$i] == $Resultado);
}
// Asignamos el resultado en la posición 8
$Valores[8] = $Resultado;
// Mezclamos los valores aleatorios y el resultado
shuffle($Valores);
// Guardamos los valores en la variable $_SESSION
for ($i = 0; $i < 9; $i++) {
$_SESSION["Captcha"][$CaptchaID][$i] = $Valores[$i];
// Si el valor actual es el resultado guardamos su posición
if ($Valores[$i] == $Resultado) $_SESSION["Captcha"][$CaptchaID]["Resultado"] = $i;
echo "<div class='ImagenCaptcha' id='ImagenCaptcha_".$CaptchaID."_".$i."'>";
echo "<img src='Captcha3_Ejemplo_Motor.php?CaptchaID=".$CaptchaID."&Img=".$i."' alt='ImagenCaptcha' />";
echo "</div>\n";
}
echo "<br /> <br />";
echo "<div>".$Valor1." + ".$Valor2." = "."<span id='ResultadoCaptcha'>???</span><span id='TxtResultado'></span></div>";
}

En el principio de la función creamos dentro de la variable "$_SESSION" la variable "CaptchaID" y un array llamado "Captcha". La idea es que si cargamos 3 pestañas con la misma página se conserven los datos para los tres captchas, por ello cada vez que se use la función "CrearCaptcha" la variable "CaptchaID" se incrementara. Dentro del array Captcha se guardaran todos los valores aleatorios y la posición del resultado correcto.

De la línea 12 a la 14 creamos los valores aleatorios para la suma, y guardamos el resultado en una variable temporal.

A partir de la línea 17 creamos un bucle que generara 8 valores aleatorios dentro de un array. Luego en la línea 24 añadimos el verdadero resultado en la posición 8 del array y en la línea 27 llamamos a la función shuffle que mezclara el array de valores de forma aleatoria.

A partir de la línea 30 creamos otro bucle que recorrerá todos los valores aleatorios y creara un div con una imagen dentro. Esta imagen apuntara al archivo "Captcha3_Ejemplo_Motor.php" utilizando el parámetro "Img" con un valor de 0 a 8, lo que generara la imagen correspondiente.

Hay que remarcar que en cada div se agrega una id "ImagenCaptcha_?_?" los interrogantes son valores, el primero es la ID actual del captcha, y el segundo corresponde a la imagen. De esta forma mandando la ID de la imagen que se ha arrastrado podremos validar el captcha ya que la misma ID nos dice la ID del captcha y la imagen.

Fijaros que guardo todos los valores aleatorios dentro de la variable $_SESSION ya que necesitaremos saberlos para pintarlos luego en las imágenes. También guardo el resultado en la variable $_SESSION.

Validación

Bien ahora ya podemos ver la función que validara el captcha :

Archivo Captcha3_Ejemplo_Motor.php función Validar
1
2
3
4
5
6
// Función que comprueba si el valor arrastrado es el correcto
function Validar($ResultadoID) {
$IDS = explode("_", $ResultadoID);
if ($_SESSION["Captcha"][$IDS[1]]["Resultado"] == $IDS[2]) echo "Validado";
else echo "Invalido";
}

Como podéis observar no tiene mucha complicación, lo primero que hago es utilizar la función explode que separa el string utilizando como delimitador el carácter "_", por lo que quedara un array que en la primera posición será "ImagenCaptcha", en la segunda posición nos dirá la ID del captcha, y en la tercera posición nos dirá el numero de imagen.

Así que únicamente comparamos si el resultado es el mismo que el numero imagen y retornamos "Validado" o "Invalido".

Ahora que ya hemos visto lo más importante de la parte php podemos pasar a ver el código JavaScript / JQuery que consta de dos funciones "IniciarCaptcha" y "ObjetoDentroObjeto". Únicamente veremos la función IniciarCaptcha :

Archivo Captcha3_Ejemplo.js función IniciarCaptcha
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
// Función que inicia los objetos del captcha para que puedan ser arrastrados utilizando JQuery
function IniciarCaptcha() {
var ImagenCaptcha = $('.ImagenCaptcha');
ImagenCaptcha.css({ 'left' : '0px', 'top' : '0px', 'cursor' : 'move' });
ImagenCaptcha.draggable('enable');
$('.ImagenCaptcha').draggable({
containment: 'window',
scroll: false,
// Al empezar a arrastrar
start : function(event, ui) {
ImagenCaptcha.css({ 'left' : '0px', 'top' : '0px', 'cursor' : 'move', 'z-index' : '1' });
ui.helper.css({ 'z-index' : '1000' });
},
// Mientras se arrastra
drag : function(event, ui) {
},
// Al terminar de arrastrar
stop : function(event, ui) {
var DentroInterrogantes = ObjetoDentroObjeto($('#ResultadoCaptcha'), ui.helper);
if (DentroInterrogantes == false) {
ui.helper.animate({"left" : 0, "top" : 0}, "fast");
$("#TxtResultado").html("");
}
else {
// Validamos el resultado
$.post("Captcha3_Ejemplo_Motor.php?ResultadoID=" + ui.helper.attr("id"),
function(data) {
if (data == "Validado") {
$("#TxtResultado").css({ "color" : "green" }).html("Captcha validado");
}
else {
ui.helper.animate({"left" : 0, "top" : 0}, "fast");
$("#TxtResultado").css({ "color" : "red" }).html("Resultado incorrecto");
}
}
);
}
}
});
}

A primera vista pude parecer un barullo, pero bastara con saber que la clase ".ImagenCaptcha" son todas las imágenes que contienen un valor aleatorio, y que luego tenemos la porción de los interrogantes donde debemos arrastrar el resultado correcto que se llama "#ResultadoCaptcha". También tenemos un área donde escribiremos el resultado de la validación que se llamara "#TxtResultado".

Para empezar en la línea 5 utilizamos la función JQuery draggable('enable'), para decirle que todos los objetos con la clase ".ImagenCaptcha" pueden ser arrastrados y soltados.

En la línea 6 volvemos a utilizar la función draggable pero esta vez para establecer sus parámetros y funciones. El parámetro "containment" se refiere al contenedor para el drag, que en este caso será toda la ventana pero se podría limitar solo a un área específica. El parámetro "scroll" es por si queremos que se haga un scroll automático al arrastrar un objeto, que en este caso no nos interesa.

En la línea 10 empezamos a definir el evento start, que será llamado cuando empecemos a arrastrar un objeto "ImagenCaptcha", en el básicamente reseteamos la posición de todos los objetos "ImagenCaptcha".

En la línea 11 establecemos el atributo "z-index" para el objeto "ui.helper" de forma que quede por encima de los demás. El objeto "ui.helper" apunta únicamente al "ImagenCaptcha" que se está moviendo, los demás no están incluidos en el.

La linea 15 aunque no es necesaria me gusta mantenerla en mis codigos para recordar el prototipo del evento drag.

En la línea 18 empieza lo bueno, esta línea se refiere al evento que salta cuando soltamos el objeto arrastrado, y es aquí donde tenemos que comprobar que se encuentre encima de los interrogantes para luego proceder a su validación.

En la línea 19 llamamos a la función "ObjetoDentroObjeto" que no hace falta mostrarla, pero que en esencia comprueba que el objeto arrastrado este dentro de los interrogantes.

En la línea 21 si el objeto arrastrado no se encuentra dentro de los interrogantes reseteamos su posición con una animación fon la funcion animate de JQuery.

En la línea 26 ya sabemos que el objeto esta dentro del área de los interrogantes por lo que utilizamos la función post para mandar la ID del objeto arrastrado al archivo "Captcha3_Ejemplo_Motor.php" mediante ajax. Dentro de la función post en la línea 27 creamos otra función que recibe el resultado de lo que se ha mandado al archivo php, que puede ser "Validado" o "Invalido", y dependiendo de esto mostramos un texto o otro.

Visto todo esto solo nos faltara ver como quedaría el archivo "Captcha3_Ejemplo.php" :

Archivo Captcha3_Ejemplo.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php include("Captcha3_Ejemplo_Motor.php"); ?>
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title>Ejemplo Captcha 3</title>
<link href='Captcha3_Ejemplo.css' rel='stylesheet' type='text/css' />
<script type='text/javascript' src='jquery-1.7.1.min.js'></script>
<script type='text/javascript' src='jquery-ui-1.8.16.custom.min.js'></script>
<script type='text/javascript' src='Captcha3_Ejemplo.js'></script>
</head>
<body onLoad="IniciarCaptcha()">
<h1>Ejemplo Captcha 3</h1>
<p>Arrastra el resultado correcto a la zona de los interrogantes.</p>
<?php
CrearCaptcha();
?>
<br />
<a href="/Blog/Captcha3">Volver a devildrey33.</a>
</body>
</html>

Conclusión

En el primer tutorial vimos lo fácil que era hackear un captcha creado únicamente con JavaScript, mientras que en el segundo tutorial solucionamos ese problema utilizando php, pero abrimos la puerta a un posible hack utilizando algún sistema de spam que sea capaz de implementar algoritmos de OCR. En este tercer tutorial aunque se implemente un algoritmo de OCR no serviría de nada, ya que la protección no es el resultado en sí, si no la ID de la imagen arrastrada. De todas formas si alguien es capaz de implementar un bot con capacidades OCR, también puede ser capaz de implementar un bot que lea las cookies, por lo que seguimos sin estar a salvo.

En definitiva, no hay nada 100% seguro. Como ya comente antes no debemos fiarnos solamente de los captchas, también estaría bien implementar un sistema que nos avise por correo si por ejemplo se reciben 10 veces más mensajes de lo normal, o algún sistema por el estilo.

Y con esto cierro los tutoriales sobre captchas, desgraciadamente no os voy a desvelar como he creado el captcha del puzzle por razones de seguridad. Como siempre podéis ver un ejemplo online sobre este tutorial, o descargar el ejemplo en vuestro ordenador.


[#10] Mallorca 11 Marzo del 2014 a las 12:09, votos 1 de 1.
Muy util, muchisimas gracias!
[#6] devildrey33 05 Mayo del 2013 a las 10:00, votos 3 de 4.
Los datos del formulario los valido tanto en JavaScript como en PHP.

Con JavaScript evitas tener que enviar datos al servidor en el caso de ser erróneos o incompletos, pero una vez mandados al servidor es muy importante validarlos también con PHP, no sea que te cuelen algo que no debería ser. Si alguien quisiera hacer algo malo, lo primero que probaría es a trastear las funciones JavaScript que definas en la web desde el depurador del navegador.

Otra cosa a remarcar es que los datos que envíes al servidor debes mandarlos con un POST, porque si los mandas con un GET el atacante ya se puede saltar la capa de seguridad que tengas con JavaScript sin despeinarse. Aunque también es posible saltarse la capa de JavaScript si usas POST, es una tarea mucho mas ardua, ya que obligas al atacante a escribir su propio código JavaScript. Por ello incluso te recomendaría incluir en el código PHP algo para detectar si se han saltado la capa de JavaScript, por ejemplo si te pasan alguno de los datos vacíos  o si alguno de los datos no cumple con los requisitos que deberían haberse validado con JavaScript, es que alguien esta trasteando mas de lo que debería  y por lo tanto lo mas recomendable seria banear esa IP. Por poner un ejemplo claro, si alguien intenta escribir un comentario con mi nombre, y el proceso llega a la capa de php sin estar logueado, sera baneado.

Algunos pensaran que poner una capa de seguridad con JavaScript es una tontería, pero por una parte ahorras peticiones al servidor, y por otra te permite preparar trampas para detectar si te viene cualquier espabilado a trastear.

En cuanto al puzzle sigue un proceso similar al ejemplo que ves aquí, pero cada pieza tiene una ID única de 10 dígitos que siempre varia, y solo el servidor sabe cual es cual. Ademas las piezas son imágenes fijas, y no se generan internamente con PHP. No te puedo detallar mucho más como funciona el captcha del puzzle por razones obvias de seguridad.

Espero que te sirva de ayuda, Saludos.
[#4] HOLA 05 Mayo del 2013 a las 0:59, votos 5 de 5.
Hola, como le haces para validar tanto el formulario como el puzzle, al momento de dar clic en: Enviar comentario
[#3] lizneydi 05 Mayo del 2013 a las 0:55, votos 2 de 5.
hola
[#2] devildrey33 12 Abril del 2013 a las 23:32, votos 0 de 1.
1 Hay un pequeño error en el ejemplo en la linea 16 de "Captcha3_Ejemplo_Motor.php"

la siguiente linea :
  if (isset($_SESSION["CaptchaID"])) {

deberia ser :
  if (!isset($_SESSION["CaptchaID"])) {

Gracias por el aviso, de todas formas por lo que he comprobado el ejemplo funciona igualmente, lo que muestra un mensaje de aviso sobre la variable $_SESSION["CaptchaID"] en el caso de no estar definida.

He actualizado el zip con el ejemplo, espero que te sirva de ayuda, saludos!

[#1] fulanito 12 Abril del 2013 a las 22:51, votos 4 de 4.
No me funcionó
mensaje de error:
PHP Notice:  Undefined index: CaptchaID in C:\\...\\Captcha3\\Captcha3_Ejemplo_Motor.php on line 20