Programação, PHP, Javascript, HTML, CSS, Python
sexta, 3 de setembro

Captcha em PHP com perguntas elaboradas

CAPTCHA é um teste de desafio cognitivo completamente automatizado para diferenciação entre computadores e humanos. Um CAPTCHA usual envolve um computador (um servidor) que pede que um usuário termine um teste. Como os computadores são incapazes de resolver o CAPTCHA, todo usuário que incorpora uma solução correta é presumidamente humano.

Completely Automated Public Turing test to tell Computers and Humans Apart

Um tipo comum de CAPTCHA requer que o usuário identifique as letras de uma imagem distorcida, às vezes com a adição de uma sequência obscurecida das letras ou dos dígitos que apareça na tela.

Por que existe o CAPTCHA?

Muitas pessoas se perguntam (e perguntam para outras) pra que serve aquelas imagens com letras inclinadas, distorcidas, riscadas que muitas vezes são difíceis de serem compreendidas, a que encontramos em páginas da internet que temos que completar ou resolver. Aquelas imagens, que complicam nossa vida, são testes para diferenciar se somos humanos ou computadores.

Existem softwares que ficam preenchendo formulários de cadastro, comentário, autenticação, votação e tudo mais que você possa imaginar. CAPTCHAs existem para impedir que esses softwares automatizados executem ações que degradam a qualidade do serviço de um sistema dado, devido à despesa do abuso ou do recurso.

Essas imagens muitas vezes são difíceis de serem compreendidas até para nós, isso porque alguns softwares tentam resolver o desafio. Se for um desafio simples o software irá resolve-lo e passar por humano.

SmartCaptcha

  1. O que é?
  2. Como usar
    1. Por sessão
    2. Por cookie
    3. Exemplo
  3. Personalizando seu CAPTCHA
    1. Fonte
    2. Cor de fundo e dos caracteres
    3. Quantidade de caracteres
  4. Código fonte
  5. Download

1. O que é?

SmartCaptcha é um CAPTCHA desenvolvido por mim que, diferente dos demais formula as seguintes perguntas:

  • Quais letras são vogais?
  • Quais letras são consoantes?
  • Quais letras são “cor”?

As letras não são distorcidas, apenas inclinadas e coloridas.

SmartCaptcha
Ver imagem dinâmica gerada pelo SmartCaptcha

2. Como usar

Para criar um CAPTCHA com SmartCaptcha você precisa instanciar a classe e executar os métodos create e show.

$captcha = new SmartCaptcha;
$captcha->create();
$captcha->show();

Isso é quase o suficiente para você ter um CAPTCHA, um teste de desafio cognitivo, ainda falta armazenar o resultado para depois compara-lo com o valor enviado pelo usuário, mesmo que esse usuário seja um computador. Há duas formas simples de armazenar: por Sessão ou Cookie.

Cada método tem sua singuliaridade. Sessão armazena os dados no servidor e cria um cookie que servirá de elo para o próximo acesso. Cookie amazena os dados no computador do usuário, que são trafegados em cada acesso/solicitação.

2.1. Por Sessão

$captcha = new SmartCaptcha;

// inicia uma sessão
session_start();

// grava a resolução do CAPTCHA na veriável de sessão "captcha"
$_SESSION['captcha'] = $captcha->create();

$captcha->show();
$captcha = new SmartCaptcha;

// grava a resolução em um cookie de nome "captcha".
// tempo de vida desse cookie é de 30 minutos
setcookie('captcha', $captcha->create(), time() - 1800, '/', '.exemplo.com');

$captcha->show();

Não importa qual método você escolha. No final das contas, o código que receber as informações enviadas precisa rever a resposta do CAPTCHA que foi armazenada em sessão ou cookie, para usar como comparação com o valor enviado e analisar se a resposta do usuário está correta, e então dizer se ele é computador ou humano.

Atenção: se você tiver optado por cookie saiba que cookies podem ser facilmente lidos por softwares, se o dado não for criptografado há grande chance de seu sistema ser burlado.

2.3. Exemplo de utilização:

<?php

// captcha-imagem.php

session_start();

$captcha = new SmartCaptcha;
$_SESSION['captcha'] = $captcha->create();
$captcha->show();

?>
<!-- formulario.html -->

<form action="enviar.php" method="post">
<img src="captcha-imagem.php"/>
<input type="text" name="captcha"/>

...
<?php

// enviar.php

session_start();

if ($_SESSION['captcha'] != $_POST['captcha']) {
	exit('Erro');
}

?>

3. Personalizando seu CAPTCHA

O SmartCaptcha é personalizável. Você pode definir fontes, cor de fundo, cores dos caracteres e quantidade de caracteres.

3.1. Fonte

O código usa a função imagettftext do PHP para criar os caracteres. Essa função usa fontes TrueType.

As constantes SMARTCAPTCHA_FONT1 e SMARTCAPTCHA_FONT2 definem as fontes utilizadas pelo SmartCaptcha. Para mudar a fonte do seu CAPTCHA mude os valores dessas constantes.

define('SMARTCAPTCHA_FONT1', 'caminho/para/minha/fonte.tff');

3.2. Cor de fundo e dos caracteres

Cor de fundo e a cor dos caracteres são customizáveis. Cuidado, não vá definir uma cor de fundo que dificulte a visualização dos caracteres e também não vá inserir cores difíceis de serem identificadas. Muitas pessoas não sabem a diferença entre azul celeste, azul turquesa e azul marinho, inclusive eu.

$captcha = new SmartCaptcha;

# branco, em RGB
$captcha->setBackgroundColor(255, 255, 255);

# Ao definir uma cor, pense sempre na pergunta:
# Quais letras são ----?
$captcha->setColor('azuis celete', 50, 153, 204);
$captcha->setColor('corais', 255, 127, 0);
$captcha->setColor('violetas', 79, 47, 79);

Pode acontecer de uma cor que você tenha definido não aparecer em alguns CAPTCHAs. Não se esqueça que as cores são escolhidas aleatoriamente.

3.3. Quantidade de caracteres

O padrão do número de caracteres é 5. Você pode aumentar ou diminuir esse número.

$captcha = new SmartCaptcha;

# CAPTCHA com 10 caracteres
$captcha->create(10);

Vale lembrar que muitos caracteres pode tornar a tarefa de responder exaustiva e propícia a erros.

4. Código fonte

<?php
/**
 * SmartCaptcha
 *
 * @author Alejandro Fernandez Moraga <moraga86@gmail.com>
 */

define('SMARTCAPTCHA_FONT1', './Arial_Black.ttf');
define('SMARTCAPTCHA_FONT2', './Arial.ttf');

class SmartCaptcha {

	/**
	 * Imagem
	 *
	 * @var Resource
	 */
	private $image;

	/**
	 * Cor de fundo do CAPTCHA
	 *
	 * @var Array
	 */
	private $backgroundColor = array(233, 234, 235);

	/**
	 * Caracteres utilizados na composição do CAPTCHA
	 *
	 * @var Array
	 */
	private $chars = array(
		'vogais' => array('A', 'E', 'I', 'O', 'U'),
		'consoantes' => array('B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z')
	);

	/**
	 * Cores utilizadas nos caracteres do CAPTCHA
	 *
	 * @var Array
	 */
	private $colors = array(
		'pretas' => array(0, 0, 0),
		'vermelhas' => array(255, 0, 0),
		'verdes' => array(0, 255, 0),
		'azuis' => array(0, 0, 255)
	);

	/**
	 * Define a cor de fundo do CAPTCHA
	 *
	 * @param int $red Escala de vermelho
	 * @param int $green Escala de verde
	 * @param int $blue Escala de azul
	 * @return void
	 */
	function setBackgroundColor($red, $green, $blue) {
		$this->backgroundColor = array($red, $green, $blue);
	}

	/**
	 * Define uma cor para os caracteres do CAPTCHA
	 *
	 * @param string $name Nome da cor no plural
	 * @param int $red Escala de vermelho
	 * @param int $green Escala de verde
	 * @param int $blue Escala de azul
	 * @return void
	 */
	function setColor($name, $red, $green, $blue) {
		$this->colors[$name] = array($red, $green, $blue);
	}

	/**
	 * Remove uma cor das cores disponíveis para os caracteres do CAPTCHA
	 *
	 * @param string $name Nome da cor
	 * @return void
	 */
	function remColor($name) {
		unset($this->colors[$name]);
	}

	/**
	 * Cria o CAPTCHA
	 *
	 * @param int $characteres Número de caracteres do CAPTCHA
	 * @return string Resposta do CAPTCHA
	 */
	function create($characters=5) {
		$pos_ini = 20;
		$chr_len = 35;

		$charst = array_keys($this->chars);
		$colors = array_keys($this->colors);

		# cria uma nova imagem
		$this->image = imagecreate($characters * $chr_len + $pos_ini * 2, 80);

		# define a cor de fundo da imagem
		imagecolorallocate($this->image, $this->backgroundColor[0], $this->backgroundColor[1], $this->backgroundColor[2]);

		# questões levantadas e suas respostas
		$avail = array();

		for ($i=0; $i < $characters; $i++) {
			# escolhe um tipo de caractere entre vogal e consoante
			$chart = $charst[rand(0, 1)];

			# escolhe um caractere do tipo previamente escolhido
			$c = $this->chars[$chart][rand(0, count($this->chars[$chart]) - 1)];

			# habilita como resposta para tipos de caracteres
			if (empty($avail[$chart]))
				$avail[$chart] = $c;
			else
				$avail[$chart] .= $c;

			# escolhe uma cor para o caractere
			$color = $colors[rand(0, count($this->colors) - 1)];

			# habilita como respota para cores
			if (empty($avail[$color]))
				$avail[$color] = $c;
			else
				$avail[$color] .= $c;

			# escreve o caractere na imagem
			imagettftext(
				# imagem
				$this->image,
				# largura
				30,
				# ângulo de inclinação
				rand(0, 25) * (rand(0, 1) ? 1 : -1),
				# posição no eixo x
				$chr_len * $i + $pos_ini,
				# posição no eixo y
				45,
				# cor do caractere
				imagecolorallocate($this->image, $this->colors[$color][0], $this->colors[$color][1], $this->colors[$color][2]), SMARTCAPTCHA_FONT1, $c);
		}

		$questions = array_keys($avail);
		# escolhe uma pergunta
		$question = rand(0, count($avail) - 1);

		# escreve a pergunta do CAPTCHA
		imagettftext($this->image, 10.5, 0, 5, 72, imagecolorallocate($this->image, 0, 0, 0), SMARTCAPTCHA_FONT2, 'Quais letras são '.$questions[$question].'?');

		return $avail[$questions[$question]];
	}

	/**
	 * Envia a imagem do CAPTCHA para o browser
	 *
	 * @return void
	 */
	function show() {
		header('Pragma: no-cache');
		header('Cache-Control: private, no-cache, no-cache="Set-Cookie", proxy-revalidate');
		header('Content-type: image/png');
		imagepng($this->image);
		imagedestroy($this->image);
	}
}

?>

Código fonte destacado em: http://www2.jarbs.com.br/codes/SmartCaptcha/SmartCaptcha.phps


5. Download

11 Comentários

  • Oliveira says:

    Excelente essa classe. Simples e funcional.

  • Yuri says:

    Prezado, muito bom porém, não consigo fazer a img aparecer. O que poderia ser?

  • Yuri says:

    Está aparecendo esse erro na pag. captcha-imagem.php:

    Fatal error: Call to a member function show() on a non-object in /var/www/formulario/captcha-imagem.php on line 9

    • Alejandro Moraga says:

      Olá Yuri!
      Respondi seu e-mail, o erro está em usar o nome da classe errado. É SmartCaptcha e não só Captcha.

      de:
      $captcha = new Captcha;

      para:
      $captcha = new SmartCaptcha;

      Abraços!
      Qualquer dúvida é só perguntar

  • Yuri says:

    Alejando, copio aqui o texto do novo e-mail que lhe mandei, agradeço pela atenção:

    troquei para new SmartCaptcha(); porém ainda não mostra.

    Da o seguinte erro quando eu acesso http://localhost/captcha-imagem.php

    Fatal error: Call to a member function show() on a non-object in /var/www/formulario/captcha-imagem.php on line 9

    Arquivos:
    —————————————————–
    captcha-imagem.php

    create();
    $captcha->show();

    ?>
    —————————————————————-

    e no index.php a img está:

    Aqui utilizo Ubuntu, instalei a biblioteca gd e a freetype já está instalada também.

    Se puder me ajudar, agradeço desde já!

  • Yuri says:

    Alejando, consegui!

    Retirei o session_start(); do arquivo captcha-imagem.php. Aí foi certinho!

    • Alejandro Moraga says:

      Yuri,

      Que bom que em parte deu certo! Quando for assim, coloca no começo dos seus códigos esse código:

      ini_set(‘display_errors’, 1);
      error_reporting(E_STRICT | E_ALL);

      Ou melhor, edita as configurações do seu php.ini para mostrar todos os erros!

      Hm, ou não está instalado o módulo de sessão ou está com algum problema. Cria um arquivo .php e coloca:

      < ?php phpinfo(); ?>

      Procura por session, tem um exemplo em: http://www9.moraga.com.br/php.php

      Se aparcer, verifica se está habilitado: Session suport: enabled

      Caso não esteja habilitado, procura pelo arquivo php.ini e dentro dele a diretiva: session.save_path
      Tem que estar apontando para algum diretório no seu computador, como C:\temp. Se estiver vazio, coloca C:\temp e reinicia o Apache!

      Abs!

  • Yuri says:

    Ixi Alejando, agora o valor que ele joga na session, não é o correto da pergunta.

    • Alejandro Moraga says:

      Consegue salvar dados na sessão? Se sim, ignora os passos que descrevi acima. Em um arquivo .php coloca < ?php print_r($_SESSION); ?>
      Vamos ver se existe sessão e dados na sessão

  • Yuri says:

    Ele imprime Array( )
    … vazio ?

    Não está iniciando as session?

    Porém no php info está Session suport: enabled.

    Lhe enviei um e-mail com os arquivos para você dar uma olhada.

    Grato!

  • Yuri says:

    Alejandro,

    Muito obrigado pela atenção e auxilio!

    Agora está tudo 100% funcionando!

    Grande abraço! E até a próxima!


Leave a Comment

Sobre o autor

Alejandro Moraga é um cientista da computação. Atualmente é o Webmaster da FAPESP. Já foi o desenvolvedor de sistemas ERP e CRM na Internetmídia. Moraga também desenvolve frameworks, ferramentas, sistemas e sites em nome da empresa METS no qual é sócio junto com sua esposa, e é um pesquisador da Inteligência Artificial.

Mais artigos

Converter array para objetos específicos em PHP
Converter array para objetos específicos em PHP

A conversão de tipos por Cast do PHP é limitada quando falamos de objetos. Para gerar objetos específicos vamos [Leia mais]

Valor monetário (float) a todo custo
Valor monetário (float) a todo custo

Valor monetário a todo custo é porque a função abaixo vai extrair de uma string e retornar os números com ponto [Leia mais]

Validar números de agências e contas correntes do Banco do Brasil
Validar números de agências e contas correntes do Banco do Brasil

Outro dia precisei validar números de agências e contas do Banco do Brasil. Para quem precisar segue as [Leia mais]

Guia de Expressões Regulares Compatíveis com Perl – PCRE
Guia de Expressões Regulares Compatíveis com Perl – PCRE

Guia PHP PCRE com funções, meta caracteres, modificadores do padrão, classes de caracteres base, quantificadores e [Leia mais]

Sequências aleatórias com nível de complexidade
Sequências aleatórias com nível de complexidade

Sequências de caracteres aleatórios são úteis para muitos propósitos. Existem funções pré-prontas, como a rand [Leia mais]