Archive for March, 2009

Captcha – Confirmação Visual

Depois de um bom tempo com o blog parado, vou voltar explicando um modo de desenvolver um captcha utilizando a biblioteca GD do php. O captcha é um teste de turing reverso, pois é a máquina que  testa se o usuário é humano. Este teste evita que sistemas automatizados enviem spans pelos formulários de seu site.
A idéia é bem simples. Vamos ter um arquivo php que gera uma imagem, podendo ser exibido como se de fato fosse uma imagem:

<img src=”geraImagemConfirmacao.php” alt=”Captcha” />

Essa imagem será gerada, e o conteúdo gravado em um cookie. Este cookie será utilizado na ação de seu formulário, quando deverá ser comparado com o texto digitado pelo usuário. Vou procurar fazer o mais simples possível:

<form action=”acaoConfirmaCodigo.php” method=”post”><img src=”geraImagemConfirmacao.php” alt=”Captcha” />
<input name=”palavraUsuario” type=”text” />

<input type=”submit” />

</form>

Salve este arquivo com um nome qualquer.

No arquivo acaoConfirmaCodigo.php, coloque algo do tipo:

<?php

$palavraUsuario = strtolower($_POST['palavraUsuario']);
$palavraGerada = strtolower($_COOKIE['palavraGerada']);

if($palavraUsuario == $palavraGerada)
echo “Confirmação visual correta.”;
else
echo “Por favor, tente novamente.”;
?>

O que este arquivo faz nada mais é do que pegar a palavra digitada pelo usuário (enviada pelo POST) e a palavra armazenada no cookie, passando ambas para a forma minúscula, e então  comparando-as.

Agora vamos à parte realmente interessante, o arquivo geraImagemConfirmacao.php:

<?php

$larguraImg = 100;
$alturaImg = 30;

$espacoMinLetrasX = 15;
$espacoMaxLetrasX = 20;

$espacoMinLetrasY = 5;
$espacoMaxLetrasY = 10;

$numLetras = 4;

$letras = array(‘2′,’3′,’4′,’5′,’6′,’7′,’8′,’9′, ‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘Z’, ‘K’, ‘M’, ‘X’);

// cria uma imagem
$imagem = imagecreate($larguraImg, $alturaImg);

/* Cores para utilizar na imagem imagem */
$cinza = imagecolorallocate($imagem,0xF8,0xF8,0xF8);
$cinza_escuro = imagecolorallocate($imagem,0xAC,0xAC,0xAC);
$vermelho = imagecolorallocate($imagem,0xFF,0×00,0×00);
$azul = imagecolorallocate($imagem,0×0F,0×93,0xFF);
$verde = imagecolorallocate($imagem,0×00,0×66,0×00);
$preto = imagecolorallocate($imagem,0×00,0×00,0×00);
$laranja = imagecolorallocate($imagem,0xFF,0×8C,0×24);

$cores = array($vermelho, $azul, $verde, $preto);

$tamanho_letras = count($letras)-1;
$tamanho_cores = count($cores)-1;

/* Escrevendo as letras… */
$palavraGerada = ”;
$x = 0;

for($i=0;$i<$numLetras;$i++){

$x += rand($espacoMinLetrasX, $espacoMaxLetrasX); //localizacao horizontal
$y = rand($espacoMinLetrasY, $espacoMaxLetrasY);

$j = rand(0,$tamanho_cores);

$k = rand(0,$tamanho_letras);
$palavraGerada .= $letras[$k];

imagestring($imagem, 5, $x, $y, $letras[$k], $cores[$j]); //escreve a letra na imagem
}

setcookie(‘palavraGerada’, $palavraGerada, time()+7200);

header(“Content-type: image/jpeg”);
imagejpeg($imagem);
imagedestroy($imagem);
?>

A primeira parte é apenas para configurar a imagem que será gerada:

$larguraImg = 100;
$alturaImg = 30;

Note que essas dimensões têm que ser compatíveis com a quantidade de letras e o espaçamento das letras que você deseja gerar, caso contrário algumas letras ficarão fora da imagem simplesmente por falta de espaço.

$espacoMinLetrasX = 15;
$espacoMaxLetrasX = 20;
$espacoMinLetrasY = 5;
$espacoMaxLetrasY = 10;
$numLetras = 4;

Aqui são determinados os intervalos das posições das letras na imagem. Por exemplo, a posição horizontal (espaço entre as letras) irá variar de 15 a 20px. Para realizar essa variação, utilizamos a função rand(min, max) do php.
E também aqui é determinada a quantidade de letras que serão geradas na imagem.

$letras = array(‘2′,’3′,’4′,’5′,’6′,’7′,’8′,’9′, ‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘Z’, ‘K’, ‘M’, ‘X’);

Esse array contém as letras que poderão ser sorteadas e aparecer na imagem. Procure evitar letras como O, I, L… e os números 0 e 1, pois podem ser confundidos com facilidade.

// cria uma imagem
$imagem = imagecreate($larguraImg, $alturaImg);

/* Cores para utilizar na imagem imagem */
$cinza = imagecolorallocate($imagem,0xF8,0xF8,0xF8);
$cinza_escuro = imagecolorallocate($imagem,0xAC,0xAC,0xAC);
$vermelho = imagecolorallocate($imagem,0xFF,0×00,0×00);
$azul = imagecolorallocate($imagem,0×0F,0×93,0xFF);
$verde = imagecolorallocate($imagem,0×00,0×66,0×00);
$preto = imagecolorallocate($imagem,0×00,0×00,0×00);
$laranja = imagecolorallocate($imagem,0xFF,0×8C,0×24);

$cores = array($vermelho, $azul, $verde, $preto);

Neste ponto, criamos a imagem, e associamos as cores que utilizaremos na imagem. Além disso, coloco no array $cores as cores que serão sorteadas para cada letra. (Note mais uma vez que você deve facilitar a vida do usuário, então utilize cores fortes nas letras).

$palavraGerada = ”;
$x = 0;
for($i=0;$i<$numLetras;$i++){

$x += rand($espacoMinLetrasX, $espacoMaxLetrasX); //localizacao horizontal
$y = rand($espacoMinLetrasY, $espacoMaxLetrasY);

$j = rand(0,$tamanho_cores);

$k = rand(0,$tamanho_letras);
$palavraGerada .= $letras[$k];

imagestring($imagem, 5, $x, $y, $letras[$k], $cores[$j]); //escreve a letra na imagem
}

Aqui é onde acontece toda a mágica. É executado um laço, com o for varrendo a quantidade de letras de sua imagem, e para cada letra sortea-se a posição x e y, dentro do intervalo configurado. Note que o intervalo x deve ser incrementado a cada iteração, caso contrário todas as letras ficariam dentro de um mesmo intervalo na horizontal (experimente trocar o += por =, para ver como fica).

Também são sorteadas a letra e sua cor, dentro do array pré-determinado. Só então a letra é gravada na imagem, com a função imagestring() do php.

setcookie(‘palavraGerada’, $palavraGerada, time()+7200);

header(“Content-type: image/jpeg”);
imagejpeg($imagem);

Finalmente a imagem é gravada no cookie e criada na tela, utilizando o cabeçalho Content-type: image/jpeg. Pronto!

Se achar conveniente, você pode utilizar outras funções da GD como imagearc, imagelin.. para “dificultar” a visualização da imagem (cuidado para não deixar a imagem imcompreensível). Pode também utilizar outra fonte para as letras, outra cor de fundo na imagem..

3 Comments