Febrero 2

2018

Hoy vamos a utilizar lo que vimos en mi ultimo blog CREAR UNA IMAGEN CON JAVASCRIPT para poder crear un CAPTCHA completo dese JAVAESCRIPT.

Que es un CAPTCHA

CAPTCHA es un pequeño programa que "verifica" que el usuario de una web es realmente una persona. Google ofrece servicios de captcha pero muchas veces, o no sabemos bien como utilizarlos, no nos interesa, o sencillamente, eres de los míos que te gusta hacer las cosas por mi mismo, ademas, de que intento "evitar" el uso del gigante Google todo lo que puedo. 

El clásico captcha es una imagen con un texto, en los inicios eran solo números, con algún tipo de "distorsión" por la que un programa de reconocimiento de texto no seria capaz de descifrar, y solo un humano, puede ver de verdad que texto se esconde en la imagen mostrada.

Ahora, precisamente Google, ofrece varios tipos de captcha, mostrando imágenes y haciendo una petición, por ejemplo, que selecciones todas en las que aparece un pájaro.  Este tipo de sistemas demuestran que quien esta utilizando la web no es un programa automatizado para rellenar formularios y enviar spam, o lo que es lo mismo, datos falsos que lo que buscan es recopilar información sobre tu sistema con tus correos de auto respuesta, por ejemplo.

Los captchas los podemos utilizar en muchas ocasiones, siempre que se requiera demostrar que el usuario es una persona humana. Pero no nos libra del spam yo mismo he recibido correos en los que han superado los sistemas de spam. Precisamente, estaba utilizando los sistemas de Google, y tras esto decidí crear mi propio sistema. Este lo hice en C# (algún día escribiré el blog para compartirlo) pero últimamente estoy haciendo muchas cosas en JAVASCRIPT y, aunque es algo mas "inseguro" es mucho mas fácil de utilizar y, sobre todo para la web, es mucho mas flexible, ya que lo podemos utilizar desde cualquier pagina web HTML y si el código de servidor es C#, PHP, PERL, (...) da lo mismo y le podemos sacar partido.

Crear un CAPTCHA sencillo con JAVASCRIPT

Empecemos por lo mas básico, ver que es lo que necesitamos.

  • Crear un texto.
  • Mostrar el texto.
  • Solicitar que se escriba en un cuadro de texto.
  • Botón para verificar que lo que hemos introducido y lo que hemos creado coincide.

Crear un texto

Esto es, inicialmente lo mas sencillo, ya que podemos hacer que sesea un texto creado aleatoria mente. Este texto lo podemos complicar todo lo que queramos, en el ejemplo que os propongo solo se utilizan caracteres de la a a la z, tanto en mayúsculas como en musculas.

function Captcha(mainCaptcha) {
var alpha = new Array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');
var i;
for (i = 0; i < 6; i++) {
var a = alpha[Math.floor(Math.random() * alpha.length)];
var b = alpha[Math.floor(Math.random() * alpha.length)];
var c = alpha[Math.floor(Math.random() * alpha.length)];
var d = alpha[Math.floor(Math.random() * alpha.length)];
var e = alpha[Math.floor(Math.random() * alpha.length)];
var f = alpha[Math.floor(Math.random() * alpha.length)];
var g = alpha[Math.floor(Math.random() * alpha.length)];
}
var code = a + ' ' + b + ' ' + ' ' + c + ' ' + d + ' ' + e + ' ' + f + ' ' + g;
setElementValue(mainCaptcha, code);
}

Como puedes observar, he declarado un Array con todas las letras y luego con un bucle (for) cargamos 7 variables para mostrar las 7 letras de las que se va a componer el captcha. Seleccionamos la posición dentro del Array de forma aleatoria y guardamos en la variable. Esto lo repito 6 veces para asegurarme de que no se repita. Luego juntamos todas las letras generadas de forma aleatoria. Este valor, en este ejemplo, lo estoy mostrando directamente al usuario.

 Verificar el texto

Tras mostrar el texto al usuario, y una vez haya introducido lo que ve en el cuadro de texto para ello, tenemos que verificar que es correcto.

function ValidCaptcha(mainCaptcha, txtInput) {
var string1 = removeSpaces(getElementValue(mainCaptcha));
var string2 = removeSpaces(getElementValue(txtInput));
if (string1 == string2) {
return true;
}
else {
Captcha(mainCaptcha);
return false;
}
}
function removeSpaces(string) {
return string.split(' ').join('');
}

Como podéis observar es muy simple, recojo el texto desde el control donde lo mostré, elimino los espacios en blanco, y comparo con el texto que introduce el usuario, por si tiene espacios en blanco, también se los elimino. Entonces devuelvo true o false con el resultado obtenido.

Ejemplo de uso

Con este pequeño código HTML podemos poner en prueba los script que acabamos de crear. 

 <body onload="Captcha('mainCaptcha');">
<table>
<tr>
<td>
Text Captcha<br />
</td>
</tr>
<tr>
<td>
<input type="text" id="mainCaptcha"/>
<input type="button" id="refresh" value="Refresh" onclick="Captcha('mainCaptcha');" />
</td>
</tr>
<tr>
<td>
<input type="text" id="txtInput"/>
</td>
</tr>
<tr>
<td>
<input id="Button1" type="button" value="Check" onclick="alert(ValidCaptcha('mainCaptcha', 'txtInput'));"/>
</td>
</tr>
</table>
</body>

Solo con esto ya tenemos lo que es la funcionalidad de nuestro captcha funcionando, pero hemos dicho que el captcha debe de ser una imagen con algo de distorsión para que no se pueda "capturar" ese texto con un programa automatizando el proceso y que nos saturen con mensajes o registros y nos bloqueen la pagina web. Es aquí donde vamos a utilizar lo visto en mi otro blog

Crear la imagen

Vamos a incluir nuestra función para crear una imagen dentro de un canvas.

function CreaIMG(texto) {
var ctxCanvas = document.getElementById('captcha').getContext('2d');
var fontSize = "30px";
var fontFamily = "Arial";
var width = 250;
var height = 50;
//tamaño
ctxCanvas.canvas.width = width;
ctxCanvas.canvas.height = height;
//color de fondo
ctxCanvas.fillStyle = "whitesmoke";
ctxCanvas.fillRect(0, 0, width, height);
//puntos de distorsion
ctxCanvas.setLineDash([7, 10]);
ctxCanvas.lineDashOffset = 5;
ctxCanvas.beginPath();
var line;
for (var i = 0; i < (width); i++) {
line = i * 5;
ctxCanvas.moveTo(line, 0);
ctxCanvas.lineTo(0, line);
}
ctxCanvas.stroke();
//formato texto
ctxCanvas.direction = 'ltr';
ctxCanvas.font = fontSize + " " + fontFamily;
//texto posicion
var x = (width / 9);
var y = (height / 3) * 2;
//color del borde del texto
ctxCanvas.strokeStyle = "yellow";
ctxCanvas.strokeText(texto, x, y);
//color del texto
ctxCanvas.fillStyle = "red";
ctxCanvas.fillText(texto, x, y);
}

Cambios a realizar

Por lo que tenemos que cambiar el primer script que escribimos y llamamos a esta función.

function Captcha(mainCaptcha) {
var alpha = new Array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');
var i;
for (i = 0; i < 6; i++) {
var a = alpha[Math.floor(Math.random() * alpha.length)];
var b = alpha[Math.floor(Math.random() * alpha.length)];
var c = alpha[Math.floor(Math.random() * alpha.length)];
var d = alpha[Math.floor(Math.random() * alpha.length)];
var e = alpha[Math.floor(Math.random() * alpha.length)];
var f = alpha[Math.floor(Math.random() * alpha.length)];
var g = alpha[Math.floor(Math.random() * alpha.length)];
}
var code = a + ' ' + b + ' ' + ' ' + c + ' ' + d + ' ' + e + ' ' + f + ' ' + g;
CreaIMG(code); //linea cambiada llamando a la función para crear la imagen en un canvas
}

Pero también tenemos que cambiar una linea en el HTML para en lugar de un IMPUT ponemos un CANVAS.

     <body onload="Captcha('mainCaptcha');">
<table>
<tr>
<td>
Text Captcha<br />
</td>
</tr>
<tr>
<td>
<canvas id="captcha"></canvas> //linea cambiada llamando a la función para crear la imagen en un canvas
<input type="button" id="refresh" value="Refresh" onclick="Captcha('mainCaptcha');" />
</td>
</tr>
<tr>
<td>
<input type="text" id="txtInput"/>
</td>
</tr>
<tr>
<td>
<input id="Button1" type="button" value="Check" onclick="alert(ValidCaptcha('mainCaptcha', 'txtInput'));"/>
</td>
</tr>
</table>
</body>

Y ahora si que ya tenemos nuestro sistema captcha completo de forma sencilla. Lo puedes adaptar a tus necesidades de forma muy simple.

El script mas completo

Hoy en día casi no se trabaja con tablas, ademas de que de esta forma tienes que escribir muchas veces mucho código, por lo que al final decidí complicar un poco mas el script inicial y hacer que el propio script cree todos los elementos HTML necesarios, ademas de utilizar etiquetas DIV basadas en BOOTSTRAP y así hacerlo todo un poco mas responsive. Aquí os dejo el código completo y el ejemplo de uso en HTML para que lo disfrutes.

function Captcha(mainCaptcha, inputName) {
var alpha1 = new Array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '!', '@', '#', ',', '.', '$', '%', '^', '&', '*', '(', ')', '-', '_', '=', '+', '[', ']', '{', '}', '"', ':', ';', '/', '?', '<', '>', '|');
var alpha2 = new Array('!', '@', '#', ',', '.', '$', '%', '^', '&', '*', '(', ')', '-', '_', '=', '+', '[', ']', '{', '}', '"', ':', ';', '/', '?', '<', '>', '|');
var alpha3 = new Array('1', '2', '3', '4', '5', '6', '7', '8', '9', '0');
var i;
for (i = 0; i < 6; i++) {
var a = alpha1[Math.floor(Math.random() * alpha1.length)];
var b = alpha2[Math.floor(Math.random() * alpha2.length)];
var c = alpha3[Math.floor(Math.random() * alpha3.length)];
var d = alpha1[Math.floor(Math.random() * alpha1.length)];
var e = alpha2[Math.floor(Math.random() * alpha2.length)];
var f = alpha3[Math.floor(Math.random() * alpha3.length)];
var g = alpha1[Math.floor(Math.random() * alpha1.length)];
}
var code = a + ' ' + b + ' ' + c + ' ' + d + ' ' + e + ' ' + f + ' ' + g;
createCookie('c', code, 0.0025); //solo 3 minutos de cookie
//crear el captcha en el div seleccionado
var contenedor = document.getElementById(mainCaptcha);
contenedor.innerHTML = '';
//primera columna
var div = document.createElement('div');
div.className = "col-lg-6 col-md-6 col-sm-6 col-xs-6";
//columna para la imagen
var div1 = document.createElement('div');
div1.className = "col-lg-10 col-md-10 col-sm-10 col-xs-10";
var captcha = document.createElement('canvas');
captcha.id = 'captcha';
div1.appendChild(captcha);
div.appendChild(div1);
//columna para el refresh
div1 = document.createElement('div');
div1.className = "col-lg-2 col-md-2 col-sm-2 col-xs-2";
var refresh = document.createElement('a');
refresh.id = "refresh";
refresh.innerHTML = '<i class="fa fa-refresh" aria-hidden="true"></i>';
refresh.href = "javascript:void(0);";
refresh.setAttribute('onclick', "Captcha('" + mainCaptcha + "');");
div1.appendChild(refresh);
div.appendChild(div1);
contenedor.appendChild(div);
//columna para el texto
div = document.createElement('div');
div.className = "col-lg-6 col-md-6 col-sm-6 col-xs-6";
div1 = document.createElement('div');
div1.className = "form-horizontal";
var input = document.createElement('input');
input.id = inputName;
input.className = "form-control";
div1.appendChild(input);
div.appendChild(div1);
contenedor.appendChild(div);
//columna para el boton
/*
div = document.createElement('div');
div.className = "col-lg-4 col-md-4 col-sm-6 col-xs-12";
var check = document.createElement('input');
check.id = "check";
check.value = "Check";
check.className = "btn btn-default pull-right";
check.setAttribute('onclick', "alert(ValidCaptcha('" + mainCaptcha + "', 'txtInput'));");
div.appendChild(check);
contenedor.appendChild(div);*/
CreaIMG(code);
}

function ValidCaptcha(mainCaptcha, inputName) {
var string1 = removeSpaces(readCookie('c'));
var string2 = removeSpaces(getElementValue(inputName));
if (string1 == string2) {
return true;
}
else {
Captcha(mainCaptcha, inputName);
return false;
}
}

function removeSpaces(string) {
return string.split(' ').join('');
}

function CreaIMG(texto) {
var ctxCanvas = document.getElementById('captcha').getContext('2d');
var fontSize = "30px";
var fontFamily = "Arial";
var width = 250;
var height = 50;
//tamaño
ctxCanvas.canvas.width = width;
ctxCanvas.canvas.height = height;
//color de fondo
ctxCanvas.fillStyle = "whitesmoke";
ctxCanvas.fillRect(0, 0, width, height);
//puntos de distorsion
ctxCanvas.setLineDash([7, 10]);
ctxCanvas.lineDashOffset = 5;
ctxCanvas.beginPath();
var line;
for (var i = 0; i < (width); i++) {
line = i * 5;
ctxCanvas.moveTo(line, 0);
ctxCanvas.lineTo(0, line);
}
ctxCanvas.stroke();
//formato texto
ctxCanvas.direction = 'ltr';
ctxCanvas.font = fontSize + " " + fontFamily;
//texto posicion
var x = (width / 9);
var y = (height / 3) * 2;
//color del borde del texto
ctxCanvas.strokeStyle = "yellow";
ctxCanvas.strokeText(texto, x, y);
//color del texto
ctxCanvas.fillStyle = "red";
ctxCanvas.fillText(texto, x, y);
}


/* EJEMPLO DE USO HTML */
/*
<script src='js/captcha.js'></script>
<script async defer type="text/javascript">
$(document).ready(function () {
Captcha('mainCaptcha', 'txtInput');
});

function ValidaWeb() {
if (ValidCaptcha('mainCaptcha', 'txtInput')) {
// tu codigo una vez validado
}
}
</script>
<div id="mainCaptcha"></div>
<button type="submit" onclick="ValidaWeb()" class="btn btn-danger pull-right"><i class="fa fa-link"></i></button>
*/

Como puedes comprobar a la hora de utilizar el código en la web apenas son unas lineas en comparación de tener que crear toda la tabla con todos los elementos. Un pequeño cambio es que estoy utilizando una cookie para almacenar al valor del texto aleatorio y el texto lo complique un poco mas, agregando números y caracteres especiales.

Ideas para mejorar el codigo

Una buena opción, es ocultar el canvas y mostrar directamente una imagen. Para ellos solo tendríamos que hacer unos pocos cambios y el mas importante, dentro del script CrearIMG  y al final del mismo habría que incluir esto:

var imagen = document.getElementById('image');
imagen.src = ctxCanvas.canvas.toDataURL();

Esta idea la obtuve de este hilo en stackoverflow .


Happy coding

#javascript #seguridad #captcha #web



0 Comentarios

Escribir un comentario

3 - 2 =



Archivo