CodeNet - все для программиста!CodeNet - 10 лет - мегавстреча!
Все для программиста!
  Статьи   Форум   Исходники   Каталог   Хостинг   IRC  
 Справочник функций

Ваш аккаунт

Логин:
Пароль:

Забыли пароль?
Регистрация

Почтовая рассылка



Подписчиков: 6553
(Из них RSS: 0)

Последний выпуск: 22.06.2010

Защита Web-форм от автоматической обработки

© Mike, 10.10.2004

Статья описывает способ реализации механизма, позволяющего бороться с автоматическим заполнением форм, ложными регистрациями и спамом через форму обратной связи.

Требования: PHP>=4.0.6, GD >=2.0.

Исходные тексты можно скачать тут.

Данная статья написана по мотивам статьи Nathan Rohler "Security Images in PHP" опубликованной на сайте #Dev Shed 9 августа 2004 года. Вообще, с начала, меня посетила мысль ее перевода, но, во первых автор выбрал интересный, но не самый тривиальный вариант решения проблемы, а во вторых, мне бы вряд ли удалость сформулировать на русском языке такое обилие мыслей.

Последнее время, в связи с распространяющейся эпидемией спама, веб-мастера, стали все чаще и чаще прятать адреса своей электронной почты (E-Mail). Многие стали использовать формы обратной связи. Но как оказалось, такую защиту можно обойти. И уже на сегодняшний день, существует огромное количество программ, предназначенных для рассылки спама, через формы обратной связи.

Наша задача - сделать так, чтобы сообщение вам смог отправить только "живой человек". Чаще всего, для этого используют небольшие картинки, на которых выводится текст. Пользователя просят продублировать этот текст в поле ввода. Если дублирование производится неверно, то форма не обрабатывается.

На сегодняшний день мне не знакомо ни одной программы, способной обойти такую защиту. Я даже на знаю ни одной программы, вообще, хоть как-то пытающуюся распознать содержимое картинки. По этому, можно смело сказать, что сегодня нет необходимости как-то искажать изображение на картинке. Но мы смотрим в будущее.

Генерация изображения

Автор предложил следующий алгоритм: сложное изображение на картинке формируется с помощью заранее подготовленных подложек. После чего на подложки выводится текст, случайным шрифтом, случайного размер и, естественно, случайного содержания.

Основной недостаток такого алгоритма заключается в том, что существующую "подложку" можно быстро вычислить. А после этого вычесть из изображения, которое необходимо распознать и таким образом получить картинку с чистым текстом. А для распознания такого текста уже сегодня существует масса программ.

Наша задача - сделать абсолютно случайную подложку, с текстом, который не то что распознать сложно, его прочитать тяжело.

Я бы предложил следующий алгоритм:

  1. Создаем подложку (для этого можно использовать алгоритм построения фракталов)
  2. Добавляем помехи - несколько случайных линий, цвета основного текста.
  3. Выводим основной текст
  4. Самое интересное - увеличиваем изображение в неровное количество раз - например, в 1.7, в 1.6
  5. Уменьшаем изображение до оригинальных размеров

Увеличивать и уменьшать изображение необходимо с использованием сглаживания, иначе даже человек не сможет прочитать текста.

Если вам кажется, что рисовать фрактал слишком сложно, то можно нарисовать простую сетку.

Принцип работы механизма

При заходе пользователя на страницу с формой, мы создаем сессию и записываем в зарегистрированную переменную случайный код:

session_start();
session_register("secret_number");

if (intval($_SESSION["secret_number"])<1000) {
    srand(doubleval(microtime()));
    $_SESSION["secret_number"]=rand(1000,9999);
    }

После того как случайный текст сгенерирован, необходимо вывести форму:

<form action="index.php" method="post">
Ваш E-Mail:<br>
<input type="text" name="email" value=""><br>
<br>
Введите код, который вы видите на картинке:<br>
<input type="text" name="secretcode" value=""><br>
<img src='code.php?<?=doubleval(microtime());?>'
 width=101 height=26 vspace=5>
<br><br>
<input type="submit">
</form>

Скрипт, обрабатывающий данные, отправленные при помощи формы, должен работать примерно следующим образом:

session_start();
session_register("secret_number");

if ($_SERVER["REQUEST_METHOD"]=="POST") {

    $error=0;
    if ($_POST["secretcode"]!=$_SESSION["secret_number"] ||
        intval($_POST["secretcode"])==0) $error=1;

    if ($error==0) {
        $_SESSION["secret_number"]=rand(1000,9999);

        // Выполняем необходимые действия с данными
        // ..
        print "Hello ".htmlspecialchars(StripSlashes($_POST["email"]));
        exit;
        }

    if ($error==1)
        print "<font color=red>Число с картинки введено неверно</font>";
    }

// Выводим форму повторно
// ...

Генерация изображения

<?
// Регистрируем переменную
session_start();
session_register("secret_number");

function mt() {
    list($usec, $sec) = explode(' ', microtime());
    return (float) $sec + ((float) $usec * 100000);
    }

header("Content-type: image/png");

// создаем изображение
$im=imagecreate(101, 26);

// Выделяем цвет фона (белый)
$w=imagecolorallocate($im, 255, 255, 255);
 
// Выделяем цвет для фона (светло-серый)
$g1=imagecolorallocate($im, 192, 192, 192);

// Выделяем цвет для более темных помех (темно-серый)
$g2=imagecolorallocate($im, 64,64,64);

// Выделяем четыре случайных темных цвета для символов
$cl1=imagecolorallocate($im,rand(0,128),rand(0,128),rand(0,128));
$cl2=imagecolorallocate($im,rand(0,128),rand(0,128),rand(0,128));
$cl3=imagecolorallocate($im,rand(0,128),rand(0,128),rand(0,128));
$cl4=imagecolorallocate($im,rand(0,128),rand(0,128),rand(0,128));

// Рисуем сетку
for ($i=0;$i<=100;$i+=5) imageline($im,$i,0,$i,25,$g1);
for ($i=0;$i<=25;$i+=5) imageline($im,0,$i,100,$i,$g1);

// Выводим каждую цифру по отдельности, немного смещая случайным образом
imagestring($im, 5, 0+rand(0,10), 5+rand(-5,5),
    substr($_SESSION["secret_number"],0,1), $cl1);
imagestring($im, 5, 25+rand(-10,10), 5+rand(-5,5),
    substr($_SESSION["secret_number"],1,1), $cl2);
imagestring($im, 5, 50+rand(-10,10), 5+rand(-5,5),
    substr($_SESSION["secret_number"],2,1), $cl3);
imagestring($im, 5, 75+rand(-10,10), 5+rand(-5,5),
    substr($_SESSION["secret_number"],3,1), $cl4);

// Выводим пару случайных линий тесного цвета, прямо поверх символов.
// Для увеличения количества линий можно увеличить,
// изменив число выделенное красным цветом
for ($i=0;$i<8;$i++)
    imageline($im,rand(0,100),rand(0,25),rand(0,100),rand(0,25),$g2);


// Коэффициент увеличения/уменьшения картинки
$k=1.7;

// Создаем новое изображение, увеличенного размера
$im1=imagecreatetruecolor(101*$k,26*$k);

// Копируем изображение с изменением размеров в большую сторону
imagecopyresized($im1, $im, 0, 0, 0, 0, 101*$k, 26*$k, 101, 26); 

// Создаем новое изображение, нормального размера
$im2=imagecreatetruecolor(101,26);

// Копируем изображение с изменением размеров в меньшую сторону
imagecopyresampled($im2, $im1, 0, 0, 0, 0, 101, 26, 101*$k, 26*$k); 

// Генерируем изображение
imagepng($im2);

// Освобождаем память
imagedestroy($im2);
imagedestroy($im1);
imagedestroy($im);
?>

Вроде все. Если что-то, оставляйте комментарии.

Комментарии пользователей (всего 26)

vitalvarna / 19 ноября 2008, 09:22:09

У меня такая проблема: файл - обработчик у меня другой, не index.php, все работает отлично, но если пользователь не правильно ввел цифры, после нажатия кнопки НАЗАД(имею ввиду из файла обработчика), пользователь возвращается на страницу, где находится форма обратной связи, а эта форма уже пуста! Пользователю надо опять набирать свой эмаил, текст сообщения и т.д. И как поставить кнопку для обновления кода, без обновления страницы и формы?

Lloid69 / 29 марта 2008, 14:26:17

Мой вариант:
<?php
session_start();

header("Content-type: image/png");

$image = imagecreate(128,64);

$background_color = ImageColorAllocate($image, rand(64,128), rand(64,128), rand(64,128));

$color1 = ImageColorAllocate($image, rand(128,255), rand(128,255), rand(128,255));
$color2 = ImageColorAllocate($image, rand(128,255), rand(128,255), rand(128,255));
$color3 = ImageColorAllocate($image, rand(128,255), rand(128,255), rand(128,255));

imagestring($image,5,40,35,$_SESSION["captcha"],$color1);

imageline($image, rand(0,128), rand(0,64), rand(0,128), rand(0,64),$color1);
imageline($image, rand(0,128), rand(0,64), rand(0,128), rand(0,64),$color2);
imageline($image, rand(0,128), rand(0,64), rand(0,128), rand(0,64),$color3);

imagesetpixel($image,rand(0,128),rand(0,64),$color1);
imagesetpixel($image,rand(0,128),rand(0,64),$color2);
imagesetpixel($image,rand(0,128),rand(0,64),$color3);
imagesetpixel($image,rand(0,128),rand(0,64),$color1);
imagesetpixel($image,rand(0,128),rand(0,64),$color2);
imagesetpixel($image,rand(0,128),rand(0,64),$color3);
imagesetpixel($image,rand(0,128),rand(0,64),$color1);
imagesetpixel($image,rand(0,128),rand(0,64),$color2);
imagesetpixel($image,rand(0,128),rand(0,64),$color3);
imagesetpixel($image,rand(0,128),rand(0,64),$color1);

imagepng($image);

imagedestroy($image);
?>

grem11n / 29 августа 2007, 17:41:22

одна из немногих статей в нете помагающая. Спасибо автору

Robinnovich / 16 июня 2007, 20:53:04

чето у меня вобще непхпло немного переделал )
<?php
header("Content-type: image/png");

$arr = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','r','s','t','u','v','x','y','z',
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','R','S','T','U','V','X','Y','Z',
'1','2','3','4','5','6','7','8','9','0');
for($i = 0; $i < 5; $i++){
$index = rand(0, count($arr) - 1);
$string .= $arr[$index];
}
$width=100;
$height=26;
$im=imagecreate($width, $height);
$w=imagecolorallocate($im, mt_rand(200,255), mt_rand(200,255), mt_rand(200,255));
$height_font = 12;
$angle = 0;
$font_file = "arial.ttf";
$box = imagettfbbox($height_font, $angle, $font_file, $string);
for ($i=0; $i<strlen($string); $i++){
$wd =(($width-$box[2]-(($width-$box[2])/2))*$i/2+rand(-5,5))+5;
imagettftext($im, $height_font, $angle, $wd, $height/1.5+rand(-3,3), imagecolorallocate($im,rand(0,128),rand(0,128),rand(0,128)), $font_file, substr($string, $i, 1));
}
for ($i=0;$i<8;$i++)
imageline($im, rand(0,100), rand(0,100), rand(0,100), rand(0,50), imagecolorallocate($im, mt_rand(200,255), mt_rand(200,255), mt_rand(200,255)));
$k=6;
$im1=imagecreatetruecolor($width*$k,$height*$k);
imagecopyresized($im1, $im, 0, 0, 0, 0, $width*$k, $height*$k, $width, $height);
$im2=imagecreatetruecolor($width, $height);
imagecopyresampled($im2, $im1, 0, 0, 0, 0, $width, $height, $width*$k, $height*$k);
imagepng($im2);
imagedestroy($im2);
imagedestroy($im1);
imagedestroy($im);
?>

tar / 05 февраля 2007, 20:00:11

Почему только в IE и при загрузке из фрейма не отображаются
цифры ?

tar / 05 февраля 2007, 19:58:59

Почему только в IE и при загрузке из фрейма не отображаются цифры

RussianSpy / 29 ноября 2006, 11:58:02

Интересно... А что же будет с теми у кого отключены куки? Получается что они не смогут работать с подобной схемой. Не стоит делать защитную картинку работа которой основана на сессиях. Да и вообще код как-то великоват - класс который я сам написал работает без сессий и там раз в 5 меньше кода...

Моя оценка: 3 с плюсом

Style)r / 09 октября 2006, 22:49:26


>>Это бред чистой воды :)
Але працює :)

mike / 08 сентября 2006, 11:07:44

Основная идея - не менять картинку для одного пользователя в момент обновления. Менять только в момент добавления.

Пользователю намного удобнее, он не должен каждый раз вчитываться в код на картинке, если форма по той или иной причине не обработана.

Код меняется только с случае успешного заполнения формы.

Цитата:

srand(doubleval(microtime()))

змінити на

srand(date("s")); (наприклад)



Это бред чистой воды :) От замены микрсекунд на секунды ничего не поменяется. Просто внутри одной секунды генератор случайных чисел будет инициализироваться одинаково.

Style)r / 07 сентября 2006, 18:28:12

Я заради вас навіть зареєструвався тут)))
Щоб картинка змінювалася (изменялась)!!! Треба (нужно строку) рядок
srand(doubleval(microtime()))

змінити на

srand(date("s")); (наприклад)

І все буде окей )))

Отображены только последние 10 комментариев. Читать все комментарии >>

Оставить комментарий

Оставлять комментарии могут только зарегистрированные пользователи.

Если вы не являетесь зарегистрированным пользователем, то вам необходимо зарегистрироваться. Регистрация бесплатна. Если вы уже зарегистрированы на CodeNet, то вам необходимо ввести логин и пароль в верхней (Alt-U) части страницы.

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог