Так ли нужен CAPTCHA?

Статья была написана для HostInfo.ru.

Часто ли вам приходится сталкиваться с работой CAPTCHA-систем? Это такие системы скриптов, задающие загадки и генерирующие трудночитаемые картинки с цифрами, буквами и прочим безобразием, и сверяющие потом то, что вы ввели, с эталоном. В частности, таким образом сайты обороняются от автоматизированной рассылки рекламы по форумам и гостевым, отделяя добропорядочных пользователей от спам-программ. Считается, что автоматический тест Тьюринга крайне осложняет жизнь спам-программам, поэтому CAPTCHA сейчас используется довольно широко, заставляя вглядываться в напрочь зашумленные надписи, гадая, что на этот раз имеется в виду - единичка или латинская строчная «эл».

Но на всякий хитрый «картинкогенератор» рано или поздно находится своя управа, и правильный ответ найдется: либо перебором, либо разбором механизма генерации картинок, либо еще через какое-нибудь «заднее крыльцо». И преграда для автоспамеров становится лишь раздражением для посетителя ресурса. Ухватываете суть проблемы? В погоне за спамоустойчивостью удобство пользователя как-то само собой отошло на второй план. Лично мне не особо по душе лозунг «это для вашей же безопасности», поэтому сегодня я поделюсь с вами устройством более дружелюбного для пользователя механизма.

Для начала давайте немного поразмышляем: в чем отличие программы от настоящего, живого человека?

Проследим поведение обычного пользователя. Человек открывает страницу с формой для добавления какой-либо записи(в гостевой, на форуме и так далее), видит поля (для простоты возьмем самые распространенные) «Имя», «E-mail», «Сообщение». Он вводит в эти поля информацию и нажимает кнопку «Отправить». И все, сообщение появляется там, где пользователь и планировал его добавление(в гостевой, на форуме и так далее).

Что делает спам-программа? Она загружает страницу, определяет, какие механизмы защиты использованы, и делает свое черное дело. Главное отличие программы от человека в том, что она воспринимает страницы как последовательность букв, слов, строк - среди слов она по заранее заданному алгоритму может найти ссылку на заветное «добавить комментарий», пройти по ней и вписать пару фраз рекламы и ссылки в соответствующие поля, так же аккуратно найти на странице кнопку отсылки сообщения и эмулировать ее нажатие. Причем многие программы автоматически находят пригодные для «гнездования» сайты и добавляют их в свою базу - для повторной «кладки».

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

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

Ограничение по частоте сообщений.
Самый простой инструмент. Необходимо следить, чтобы с одного и того же IP не было по десятку сообщений в минуту - ясно, что вряд ли человек с такой частотой будет высказывать какие-то умные мысли. Этот прием, конечно, толком не избавляет от спама, но сокращает его поток. Главное тут - правильно подобрать интервал: и большой плохо, и короткий нехорошо. По собственному опыту минуты три - в самый раз.

Блокирование сообщений по ключевым словам.
Тоже довольно простой фильтр: не пропускаются до публикации сообщения, имеющие среди текста слова, которых точно не будет содержать «добропорядочный» текст. Заодно можно отфильтровывать не только рекламу, но и мат.

Замена символов «двойниками».
Случайная замена на странице букв на схожие по написанию символы (для человека все равно, а для разбора и поиска по шаблону - помеха). При этом в ссылках, ведущих на добавление комментариев или на регистрацию, буквы меняются на схожие по написанию, но имеющие другой код. Например, русские «а», «о», «р» на английские «a», «o» и «p» соответственно. Это немного усложняет поиск потенциальных сайтов-жертв.

Изменение имен полей ввода.
Многие программы ищут в странице поля ввода со стандартными именами: «name», «user», «nick», «email», «mail» и так далее. Чтобы доставить программе некоторое неудобство, лучше называть поля как-нибудь нестандартно. Например, «kndfjero».

Замена надписей картинками с текстом.
Если реализован предыдущий шаг, то есть шанс, что программа не сдастся, не найдя привычных имен полей, а попытается проанализировать находящиеся рядом надписи, пытаясь выяснить, к чему какое поле относится. Смена поясняющих надписей на картинки с текстом сильно усложняет эту задачу в плане решения на программном уровне, а уж пиктограммы и вовсе делают ее практически невыполнимой. Визуально, с точки зрения пользователя, на странице ничего не меняется: какая ему разница, как набран текст на сайте, картинкой или текстом? С другой стороны, достаточно спамеру-человеку один раз посетить такую страницу и «врукопашную» посмотреть что где - и все, программе уже задан шаблон для этой страницы, она «знает», куда что писать и куда нажимать для отправки.

Встраивание «полей-приманок».
Раз программа ищет поля «name», «message» и тому подобные, почему бы не «обрадовать» ее этими полями? Это сделать довольно просто, например так:
Имя:
<input type=text name=name style="visibility: hidden; width: 1px;" value="">
<input type=text name=nlconpiqo value="">

E-mail:
<input type=text name=email style="visibility: hidden; width: 1px;" value="">
<input type=text name=ummsklcqu value="">
При отображении такие поля не видны, а программа их находит и обрабатывает. От особенно «умных« программ «visibility: hidden» прячется в CSS:
<style type="text/css">
input.foul {
visibility: hidden;
width: 1px;
height: 1px;
}
</style>

...
Имя: <input type=text name=name class=foul value=""> <input type=text name=nlconpiqo value=""> E-mail: <input type=text name=email class=foul value=""> <input type=text name=ummsklcqu value="">

Построение формы с помощью JavaScript.
Этот метод делает анализ формы еще более сложным: программе необходимо найти и выполнить код JavaScript, чтобы получить форму, которую следует заполнить:
<div id=mform> Включите, пожалуйста, поддержку JavaScript в браузере </div>

...
<script language=javascript> myform=document.getElementById("mform"); mystr="<form method=\"post\" name=\"som"; mystr=mystr+"eform\" action=\"somescript.php\">И"; mystr=mystr+"мя: <input type=text name=ni"; mystr=mystr+"ck value=\"\"><br>E-ma"; mystr=mystr+"il: <input type=text name=ema"; mystr=mystr+"il value=\"\"><br>Сообщение: <texta"; mystr=mystr+"rea cols=35 rows=8 name=ms"; mystr=mystr+"g></textarea><br><input type=bu"; mystr=mystr+"tton value=\"отправить\" onclick=\"somef"; mystr=mystr+"orm.submit()\"><br></form>"; myform.innerHTML=mystr; </script>
Наличие механизмов обучения и делает автоспамеров особенно «въедливыми»: один раз программе показали, как надо действовать при данной ситуации, она запомнила, и все - разгребай, хозяин, спам на форуме. Значит, надо сделать так, чтобы страница каждый раз была разной, верно?

Отлично, тогда переходим к следующим, более изощренным методам.

Генерирование случайных имен полей ввода.
Это просто усовершенствованный метод изменения имен полей ввода: имена изменяются не один раз (при написании скрипта), а при каждом вызове страницы. Тут есть одна тонкость: поскольку имена раздаются динамически, надо как-то известить получающий данные скрипт, какая переменная за что отвечает. Например, ввести дополнительное скрытое поле, в которое записать шифровку, в которой указать, какие поля обрабатываются:
<form method="post" name="jdfjvb" action="somescript.php">
Имя: <input type=text name=nlconpiqo value="">
E-mail: <input type=text name=ummsklcqu value="">
Сообщение: <textarea cols=35 rows=8 name=thtqnrnee></textarea>
<input type=button value="отправить" onclick="jdfjvb.submit()">
<input type=hidden name=temp value="piqonlconbnhrshelcquummskrneethtqn">
</form> 
В somescript.php происходит «разбор» переменной-шифровки temp с именами и выуживание из переданных данных нужной информации:
$war = decrypt_string($temp);
$name = trim($_POST[$war[0]]);
$email = trim($_POST[$war[1]]);
$message = trim($_POST[$war[2]]);
Функция decrypt_string отвечает за расшифровку переданных данных и не ограничена ничем, кроме вашей фантазии. Алгоритм шифрования можно в любой момент изменить, например, если спамер просто использует единожды загруженную страницу для отправки повторных сообщений.

Генерирование принимающего скрипта.
Усложняем предыдущий случай: кроме случайных имен будем создавать на диске и принимающий скрипт:
<form method="post" name="jdfjvb" action="shebnhr.php">
Имя: <input type=text name=nlconpiqo value="">
E-mail: <input type=text name=ummsklcqu value="">
Сообщение: <textarea cols=35 rows=8 name=thtqnrnee></textarea>
<input type=button value="отправить" onclick="jdfjvb.submit()">
<input type=hidden name=temp value="piqonlconbnhrshelcquummskrneethtqn">
</form>
На диске, соответственно, создается shebnhr.php - на этот раз введенные данные отправятся на обработку этому файлу. Таким образом, можно иногда менять поля «Имя», «E-mail» и «Сообщение» местами, (что нанесет ущерб удобству пользователя не в пример меньше, чем корявые буквы на рисунке, но окончательно собьет с толку программу), менять механизм шифрования «на лету» - ведь принимающая часть генерируется специально под данные имена полей, под данную форму. Переданная запись сохраняется в гостевой, а после принимающий скрипт стирает сам себя. Соответственно, использовать для отправки данных вторично невозможно чисто физически: файла, к которому обращались в первый раз, давно нет. Конечно, не знаю, насколько оправдано применение решения для сайта в 3000 уникальных посетителей и 600-700 сообщений в день, ибо получается нагрузка на жесткий диск из-за создания и стирания файлов.

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

Увы, против спамеров, вбивающих обявления вручную, противоядий вовсе нет (кроме, разве что, фильтрования сообщений по содержимому). С другой стороны, целью этой статьи не было выведение универсального оружия против спама. Целью было показать, что тест Тьюринга - не панацея, а иногда - только причина раздражения. И что в большинстве случаев можно запросто обойтись без него, не заставляя пользователя делать лишние операции. Достаточно только хотя бы немного изменить, придать индивидуальности файлам - из-за одного-единственного сайта переделывать ориентированную на типовые решения и распространенные CMS спам-программу никто не будет.


24 февраля 2007

Расширено, дополнено и опубликовано на HostInfo.ru 22 марта 2007.


« вернуться к списку текстов
« комментарии ()


© сайт разработан и поддерживается мной.