Captcha на Perl


Captcha на Perl
В статье описан метод защиты от автоматического заполнения и отправки формы с сайта путем динамической генерации картинки с кодом и подтверждения правильности ввода.
В статье описан метод защиты от автоматического заполнения и отправки формы с сайта путем динамической генерации картинки с кодом и подтверждения правильности ввода.

Captcha: Как это работает

Когда происходит заполнения полей с данными, система просит пользователя указать код, который он видит на картинке. После проверки правильности ввода происходит решение - пускать клиента в систему или нет. Большой плюс такого метода - надежность. "Вскрыть" картинку и найти в ней цифры не так-то просто, потому что здесь придется писать сложный анализатор изображения.

Алгоритм работы Captcha на Perl

  • генерируем случайное число с заданным количеством разрядов или слово из словаря (будет использоваться как надпись на картинке) - пароль
  • создаем случайный код сессии
  • во временном каталоге создаем файл с именем сессии, внутри содержится сгенерированный пароль
  • не забываем удалять старые файлы сессий, время жизни которых истекло - неудачные попытки авторизации
  • в заполняемой пользователем форме вставляем hidden поле с кодом сессии и поле ввода пароля
  • генерируем и показываем на странице подготовленную картинку с паролем (делаем ее трудночитаемой для возможных анализаторов, но понятной человеку)
  • после отправки заполненной формы сравниваем содержимое файла сессии с введенным паролем, если значения совпадают - значит форму заполняет человек, вносим данные
  • удаляем файл завершившейся сессии

Замечания по реализации

Сначала была мысль не использовать файлы сессий, а передавать в форме в hidden поле зашифрованный по MD5 пароль или обойтись просто созданием временных файлов с именами-значениями пароля, и проверять только их наличие. Но решил все же делать с запасом надежности. Случайный пароль для картинки
$kol_digit=5; 
@pass_chars=(0..9);

srand(); 
$password=join("", @pass_chars[map {rand @pass_chars}(1..$kol_digit)]); 
Не забываем раскручивать генератор случайных чисел. В примере пароль создается исключительно из цифр, но для повышения безопасности можно добавить и буквы
@pass_chars=("A".."Z", "a".."z", 0..9, qw(% ! $ % ^ & *)); 
Также можно использовать слова и куски текста из словаря.

Шифрованный код сессии

Используется модуль Perl Digest::MD5 (http://search.cpan.org/dist/Digest-MD5/) Уникальная строка для шифрования - текущее время в raw-формате, а также процесс скрипта.
$salt=Digest::MD5->new; 
$hash = $salt->add(time().$$); 
# шифруем методом ASCII-HEX 
$session_code=$hash->hexdigest; 

Подготовленная картинка с паролем

Картинка отображается на странице как STDOUT работы небольшого скрипта, генерирующего картинку, код сессии передаем скрипту как параметр
<img align="right" 
src="/cgi-bin/anti_robot_img.cgi?code=<session_code>" 
border=1 
alt=""> 
Если параметр не задан - генерируется картинка со случаным паролем. Для создания и вывода картинки используется модуль Image::Magick (http://www.imagemagick.org/) Слово пароля посимвольно выводим на изображение, каждая буква отображается со случайным сдвигом по горизонтали и вертикали, а также вращением. После этого "зашумляем" изображение - сверху в случайных местах разбрасываем разноцветные точки.

Модуль

# введи число, изображенное на картинке
# защита от заполнения формы с данными роботами
# Vladimir Maximenko  [email protected]

package anti_robot_img;

use Digest::MD5;
use Image::Magick;

require Exporter;
@ISA = qw( Exporter );
@EXPORT = qw(

anti_new_session
get_pass_str
get_pass_img
check_anti_robot
);

$flag_dir="/tmp";
$time_flag_live=60*60;

# генерация пароля
$kol_digit=5;
@pass_chars=(0..9);
#@pass_chars=("A".."Z", "a".."z", 0..9);

# возвращаемая картинка
$img_width=80;
$img_height=30;
$noise_pixels=100;

# строка случайных символов для картинки
sub get_pass_str
{
 srand();
 return join("", @pass_chars[map {rand @pass_chars}(1..$kol_digit)]);
}

# шифрованый код сессии
sub enctypt_session_code
{
 my $salt=Digest::MD5->new;
# уникальная строка для шифрования
 my $hash = $salt->add(time().$$);

# шифруем методом ASCII-HEX
 return $hash->hexdigest;
}

# удаляем старые сессии
sub del_old_session
{
 opendir (DIR, $flag_dir);
  my @flag_files=readdir (DIR);
 closedir (DIR);

 shift(@flag_files);shift(@flag_files);
 foreach $f (@flag_files)
 {
  if ($f =~ /\.anti_robot$/)
  {
   my $mtime=(stat("$flag_dir/$f"))[9];
   if ($mtime) { $check_pass.=$_; }
  close(FILE);

  if ($pass_str eq $check_pass) { return(1); }
  else { return(0); }
 }
 else { return(0); }
}

# новая сессия, готовим ловушку для робота
sub anti_new_session
{
# новые случайные значения
 my $new_pass_str=&get_pass_str();
 my $new_session_code=&enctypt_session_code();

# удаляем старые сессии
 &del_old_session();

# пишем в проверочный файл
 open(FILE,">$flag_dir/$new_session_code.anti_robot") || die "$!";
  print FILE $new_pass_str;
 close(FILE);

 return $new_pass_str, $new_session_code;
}

# выводим подготовленную картинку с паролем
sub get_pass_img
{
# из номера сессии достаем пароль
 if  ($_[0] and -e "$flag_dir/$_[0].anti_robot")
 {
  $pass_str="";
  open(FILE,"$flag_dir/$_[0].anti_robot") || die "$!";
   while () { $pass_str.=$_; }
  close(FILE);
 }
 else { $pass_str=&get_pass_str(); }

# новая картинка
 $image = Image::Magick->new;
 $image->Set(size=>$img_width."x".$img_height);
 $image->ReadImage('xc:white');

# выводим пароль с искажением текста
 my $step_x=15;
 my $base_x=5;
 my $base_y=$img_height-5;
 foreach my $letter (split(//, $pass_str))
 {
# сдвиг и вращение буквы
  my $sdvig_x=int(rand(4))-2;
  my $sdvig_y=int(rand(10))-5;
  my $rotate=int(rand(60))-30;

  $image->Annotate(
         antialias => 'true',
         pointsize => 22,
         x	=> $base_x+$sdvig_x,
         y	=> $base_y+$sdvig_y,
         rotate => $rotate,
         fill	=> 'black',
         encoding=> 'windows1251',
         text	=> $letter
  );

  $base_x+=$step_x;
 }

# добавляем шум (для затруднения восприятия)
 for my $i (1..$noise_pixels)
 {
  my $rnd_x=int(rand($img_width));
  my $rnd_y=int(rand($img_height));
  $image->Set("pixel[$rnd_x,$rnd_y]"=>"red");
 }

# выводим картинку
  binmode STDOUT;
  $image->Write('gif:-');
}

1;
Можно еще добавить вывод букв разными шрифтами и цветами, а также использовать разноцветный фон (например кусочки фотографий)

Владимир Максименко

Оценить Статью:  
1   2   3   4   5   6   7   8   9   10    

« Назад
SAPE все усложнил?

MainLink - простая и прибыльная продажа ссылок!

Последние поступления:

Стишки пирожки про Олега⁠⁠

Размещена 20 июня 2024 года

Олег купил презервативы
Проник в семидесятый год
И подарил их папе с мамой
Такой нелепый суицид

читать далее…

Размещена 10 августа 2020 года

Я по ТВ видел, что через 10 лет мы будем жить лучше, чем в Германии...
Я не понял, что это они с Германией сделать хотят?!

читать далее…

ТехЗадание на Землю

Размещена 14 марта 2018 года

Пpоект Genesis (из коpпоpативной пеpеписки)

читать далее…

Шпаргалка по работе с Vim

Размещена 05 декабря 2017 года

Vim довольно мощный редактор, но работа с ним не всегда наглядна.
Например если нужно отредактировать какой-то файл например при помощи crontab, без знания специфики работы с viv никак.

читать далее…

Ошибка: Error: Cannot find a valid baseurl for repo

Размещена 13 сентабря 2017 года

Если возникает ошибка на centos 5 вида
YumRepo Error: All mirror URLs are not using ftp, http[s] or file.
Eg. Invalid release/

читать далее…