Справочник функций

Ваш аккаунт

Логин:
Пароль:
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:
Убрать блок ВКонтакте навсегда

Последние темы форума

Показать новые сообщения »

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



Подписчиков: 19086
Последний выпуск: 12.01.2012

Защита 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);
?>

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

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

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

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

Комментарии

1. vitalvarna / 19 ноября 2008, 09:22:09
Мне нравитсяМне не нравится

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

2. 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);
?>

3. grem11n / 29 августа 2007, 17:41:22
Мне нравитсяМне не нравится

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

4. 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);
?>

5. tar / 05 февраля 2007, 20:00:11
Мне нравитсяМне не нравится

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

6. tar / 05 февраля 2007, 19:58:59
Мне нравитсяМне не нравится

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

7. RussianSpy / 29 ноября 2006, 11:58:02
Мне нравитсяМне не нравится

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

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

8. Style)r / 09 октября 2006, 22:49:26
Мне нравитсяМне не нравится


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

9. mike / 08 сентября 2006, 11:07:44
Мне нравитсяМне не нравится

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

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

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

Цитата:

srand(doubleval(microtime()))

змінити на

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



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

10. Style)r / 07 сентября 2006, 18:28:12
Мне нравитсяМне не нравится

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

змінити на

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

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

11. HerSystem / 27 июня 2006, 11:38:38
Мне нравитсяМне не нравится

И в форме уже просто:
<form action="index.php" method="post">
Введите код, который вы видите на картинке:<br>
<input type="text" name="secretcode"><br>
<img src='code.php'><br>
<input type="submit">
</form>

12. HerSystem / 27 июня 2006, 11:25:50
Мне нравитсяМне не нравится

Чёто мне кажется нагрузили тут лишнего! + функциии, которые в php4-5 уже не нужны! Может я и ошибаюсь.. Вот что я сделал(точнее сократил) + при нажатии F5 цифры меняются:
Из code.php удаляем:
session_register("secret_number");
function mt() {
list($usec, $sec) = explode(' ', microtime());
return (float) $sec + ((float) $usec * 100000);
}

А в index.php заменяем весь код проверки и генерации на:
session_start();
if ($_POST)
{
if($_POST["secretcode"] == $_SESSION["secret_number"])
{
echo "Код принят";
exit;
}
else {echo "Вы ввели неправильно номер на картинке";}
} else {$_SESSION["secret_number"] = rand(1000,9999);}
//А дальше форму

Что я не правильно сделал?

13. mike / 13 июня 2006, 23:49:57
Мне нравитсяМне не нравится

Цитата:

Что-бы избежать отображения одного и того-же кода при обновлении страницы (F5).

А чем плохо то что код во время обновления страницы не меняется ?

14. serijvolk / 13 июня 2006, 20:14:24
Мне нравитсяМне не нравится

Что-бы избежать отображения одного и того-же кода при обновлении страницы (F5).

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>";
}

srand(doubleval(microtime()));
$_SESSION["secret_number"]=rand(1000,9999);
// Выводим форму повторно
// ...

15. Danox / 02 марта 2006, 20:32:03
Мне нравитсяМне не нравится

Народ всегда будет один код но когда вы ведеты его верно код сменится на следущий раз =)!!!

16. Ген / 03 февраля 2006, 14:12:56
Мне нравитсяМне не нравится

Я использую ваш код и
почему-то всё часто выводит число 8561 и на всех компьютерах также

17. mike / 20 января 2006, 20:19:59
Мне нравитсяМне не нравится

<blockquote><small>Цитата:<hr size=1>у меня должно быть
<form action="send.php" method="post">

а скрипт требует что в форме стоял
<form action="index.php" method="post">[/quote]Так и нужно передавать в send.php. А уже в него встраивать проверку верности числа.

18. mike / 20 января 2006, 20:19:12
Мне нравитсяМне не нравится

<blockquote><small>Цитата:<hr size=1>Число на картинке реально одно и то же всегда :(
Даже тут, в комментариях... Как быть? :([/quote]Оно и должно быть всегда одно и то же. Изменится оно только после добавления комментария. У других пользователей он другое, и тоже изменяется только при добавлении комментария.

19. Гарик / 20 января 2006, 17:42:33
Мне нравитсяМне не нравится

Привет всем.

Работает замечательно. Но я его не как не могу совместить со с моей формой.
Дело в том что у меня форма состоит из двух файлов
1. Сома форма (index.php) и
2. Обработчик (send.php)

Проблема состоит в том что когда я вставляю код со скрипта в
мою форму, получаетца противаречие.
у меня должно быть
<form action="send.php" method="post">

а скрипт требует что в форме стоял
<form action="index.php" method="post">

А как же данные передавать в (send.php)?
Джентелмены подскажыте как решыть эту зодачу.

Гарик.

20. Wormsik / 02 января 2006, 15:14:24
Мне нравитсяМне не нравится

Сорки за пред. сообщение. Что-то многовато символово можно вставлять. Вам так не кажется? По теме: число на картинке реально одно и то же всегда :(
Даже тут, в комментариях... Как быть? :(

21. МММ((( / 18 декабря 2005, 21:55:48
Мне нравитсяМне не нравится

Эхх,я таки не смог вставить это к себе на сайтик,как ни стрался постоянно выводилось 4071,
эххх(((

22. Илья / 03 августа 2005, 08:59:54
Мне нравитсяМне не нравится

А вот и ссылка:
http://www.xakep.ru/magazine/xa/073/120/1.asp

23. Илья / 22 июля 2005, 12:18:07
Мне нравитсяМне не нравится

<blockquote><small>Цитата:<hr size=1>
На сегодняшний день мне не знакомо ни одной программы, способной обойти такую защиту. Я даже на знаю ни одной программы, вообще, хоть как-то пытающуюся распознать содержимое картинки.
[/quote]
Такие программы есть, и их очень легко сделать самому. В каком то из номеров журнала "Хакер" написали как это сделать на Perle.
Именно таким образом и поломали защиту у "пчеловодов"(BeeLine).

24. Some / 17 июля 2005, 02:31:47
Мне нравитсяМне не нравится

Вряд-ли. Т.к. некоторые программы могут иметь преднастраиваемый интерфейс, так что только ручное подтверждение гарантирует неавтоматизированность процесса ... Сам такие пишу ...

25. AntonyNight / 22 мая 2005, 16:11:51
Мне нравитсяМне не нравится

а как позключить GD >=2.0

26. Lobotomy / 22 апреля 2005, 10:29:17
Мне нравитсяМне не нравится

Думаю можно придумать более удобные способы защиты кроме как с помощью изображений.
Программы автозаполнения обычно сканируют страницу на стандартные имена полей (<input name="email"...), которые редко отличаются названиями даже на разных сайтах.
Что если генерировать не текст на изображении, а название поля?
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог