БлогNot. PHP: Делаем простую капчу с цифрами

PHP: Делаем простую капчу с цифрами

Обычно в наше время для капчи ("антиспамового кода", вводимого на многих страницах Интернета) генерируют картинки средствами библиотеки GDLib, получая код вроде этого (взят из одного старого проекта):

<?php
$letters = array();
function generate_key()
{
	global $letters;
	$chars = 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', '1', '2',
		'3', '4', '5', '6',
		'7', '8', '9', '0',
	);
	$count = count($chars) - 1;
	$key = array();
	for($i = 0; $i < 2; $i++)
	{
		$key[ $i ] = '';
		for($j = 0; $j < 3; $j++)
		{
			list($usec, $sec) = explode(' ', microtime());
			mt_srand((float) $sec + ((float) $usec * 1000000));
			$letter = $chars[ mt_rand(0, $count) ];
			if ( isset($letters[ $letter ]) )
			{
				--$j;
			}
			else
			{
				$letters[ $letter ] = $letter;
				
				$key[ $i ] .= $letter;
			}
		}
	}
	
	return implode('', $key);
	
}
$img_x = 200;
$img_y = 50;
$font = '/font.ttf';
$content = generate_key();
session_start();
session_register("antibot");
$_SESSION['antibot'] = $content;
$img = imagecreate($img_x, $img_y);
$colours = array();
$colours['white'] = imagecolorallocate($img, 255, 255, 255);
$colours['grey'] = imagecolorallocate($img, 230, 230, 230);
$colours['black'] = imagecolorallocate($img, 0, 0, 0);
$number_of_x_lines = ($img_x - 1) / 6;
$number_of_y_lines = ($img_y - 1) / 6;
for($i = 0; $i < $number_of_x_lines; $i++){
 imageline($img, $i * $number_of_x_lines, 0, $i * $number_of_x_lines, $img_y, $colours['grey']);}
for($i = 0; $i < $number_of_y_lines; $i++){
 imageline($img, 0, $i * $number_of_y_lines, $img_x, $i * $number_of_y_lines, $colours['grey']);}
$txt_bbox = imagettfbbox(20, 0, $font, $content);
$sx = ($img_x - ($txt_bbox[2] - $txt_bbox[0])) / 2;
$sy = ($img_y - ($txt_bbox[1] - $txt_bbox[7])) / 2;
$sy -= $txt_bbox[7];
header('Content-type: image/png');

imagettftext($img, mt_rand(14, 18), mt_rand(-10, 10), $sx, $sy, $colours['black'], $font, $content);
imagepng($img);
imagedestroy($img);
?>

Часто так сделать невозможно (отключена или недоступна GDLib, просто не хочется стрелять из пушки по воробьям) и т.п. Тогда можно сделать гораздо проще, написав скрипт, самостоятельно кодирующий GIF'ки капчи прямо в теле скрипта с помощью base64.

Для простоты ограничимся капчами, состоящими только из цифр и напишем следующий демо-скрипт:

<?php
//Совсем простая капча

$captha_length=4; // Количество символов в коде
$captcha_random_seed="543129"; // Случайное число для защиты; поставьте своё

if (isset($_REQUEST['image'])) {
 function write_image_number ($num) {
  $number="R0lGODlhCgAMAIABAFNTU////yH5BAEAAAEALAAAAAAKAAwAAAI"; // Заголовок GIFки в base64
  if ($num=="0") { $len="63"; $number.="WjIFgi6e+QpMP0jin1bfv2nFaBlJaAQA7";}
  if ($num=="1") { $len="61"; $number.="UjA1wG8noXlJsUnlrXhE/+DXb0RUAOw==";}
  if ($num=="2") { $len="64"; $number.="XjIFgi6e+QpMPRlbjvFtnfFnchyVJUAAAOw==";}
  if ($num=="3") { $len="64"; $number.="XjIFgi6e+Qovs0RkTzXbj+3yTJnUlVgAAOw==";}
  if ($num=="4") { $len="64"; $number.="XjA9wG8mWFIty0amczbVJDVHg9oSlZxQAOw==";}
  if ($num=="5") { $len="63"; $number.="WTIAJdsuPHovSKGoprhs67mzaJypMAQA7";}
  if ($num=="6") { $len="63"; $number.="WjIFoB6vxmFw0pfpihI3jOW1at3FRAQA7";}
  if ($num=="7") { $len="61"; $number.="UDI4Xy6vtAIzTyPpg1ndu9oEdNxUAOw==";}
  if ($num=="8") { $len="63"; $number.="WjIFgi6e+QpMP2slSpJbn7mFeWDlYAQA7";}
  if ($num=="9") { $len="64"; $number.="XjIFgi6e+QpMP0jinvbT2FGGPxmlkohUAOw==";}
  header("Content-type: image/gif"); 
  header("Content-length: $len");
  echo base64_decode($number); 
 }

 // Вывод закодированных изображений на экран
 if (array_key_exists('image', $_REQUEST)) { 
  $num=$_REQUEST['image'];
  for ($i=0; $i<10; $i++) { 
   if (md5($i+$captcha_random_seed)==$num) { 
    write_image_number($i); exit; //Вывод одной цифры и выход
   } 
  } 
 }
 exit;
}

//Печатаем заголовок документа
echo '
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
 <meta content="text/html; charset=Windows-1251" http-equiv="content-type">
 <title>Простая капча на PHP</title>
</head>
<body>';

$captcha_key=''; 
//mt_srand(time()+(double)microtime()*1000000); //Не нужно с PHP 4.2.0
print"<form action=\"".$_SERVER["PHP_SELF"]."\" method=\"post\">Защитный код: ";
for ($i=0; $i<$captha_length; $i++) {
 $snum=mt_rand(0,9); 
 $psnum=md5($snum+$captcha_random_seed);
 echo '<img src="'.$_SERVER["PHP_SELF"]."?image=$psnum\" border=\"0\" alt=\"\">\n";
 $captcha_key.=$snum;
}
$captcha_key=md5("$captcha_key+$captcha_random_seed");
print" <input name=\"captcha_key\" type=\"hidden\" value=\"$captcha_key\">
<input name=\"user_captcha_number\" type=\"text\" maxlength=\"$captha_length\" size=\"$captha_length\"> 
(введите число, указанное на картинке) 
<input type=\"submit\" name=\"action\" value=\"OK\"></form>";

//проверка:
if (isset($_REQUEST['action'])) { //была нажата кнопка
 $errormsg="<font color=red>Введённый код неверен!</font>";
 if (!isset($_POST['user_captcha_number']) or !isset($_POST['captcha_key'])) {
  print "$errormsg"; exit;
 }
 $user_captcha_number=$_POST['user_captcha_number'];
 $captcha_key=$_POST['captcha_key'];
 if (md5("$user_captcha_number+$captcha_random_seed")!=$captcha_key) { print "$errormsg"; exit; }
 print "Всё верно</body></html>";
}
?>

Так как скрипт вызывает сам себя для получения очередной картинки-цифры, нельзя перед выводом картинки ставить какой-нибудь HTML-код. А вот перед ветвью для вывода формы - можно, как и сделано в примере. Не уверен также, что уже нет ботов, читающих эти довольно стандартные цифровые последовательности. Попозже, может, сделаю из этого простенький модуль. Конечно, для его работы в форме всё равно нужно будет указывать текстовое поле user_captcha_number (число, введённое пользователем), скрытое поле captcha_key (зашифрованный "правильный ответ") и кнопку action (отправка формы). Разумеется, названия полей формы HTML можно изменить, тогда изменятся и названия соответствующих переменных в теле скрипта.

 Этот пример в работе

Почему в IE 7-8 сообщения "Код неправилен" или "Всё верно" не выводятся при отправке формы клавишей Enter, а не кнопкой формы ОК - подумаю, вроде бы, форма простая и сделана теоретически правильно...

P.S. Фиг его знает, этот IE, печать массива $_REQUEST строкой

print_r ($_REQUEST);
дала в нём следующее:

Array ( [captcha_key] => 0d38b62fe1d3fb3cebe67a35c627c59a [user_captcha_number] => 4089 [__utma] => 
96992031.150883131.1284642699.1285238584.1285244288.8 [phpbb2mysql_data] => 
a:2:{s:11:\"autologinid\";s:0:\"\";s:6:\"userid\";i:-1;} )

 Окончание статьи и исправление - на сайте

24.05.2011, 16:27 [17300 просмотров]


теги: программирование random php числа безопасность браузеры спам ie

К этой статье пока нет комментариев, Ваш будет первым