Как стать автором
Обновить

Formcha. Антибот с человеческим лицом?

Время на прочтение5 мин
Количество просмотров2.7K

Предыстория


Однажды я пытался зарегистрироваться на одном форуме разработчиков. Долго пытался, использовал почти весь свой запас ругательств в адрес админа, который очень сильно перестарался с настройками captcha'и. Зарегистрировался только с надцатого раза и истратил кучу полезного времени на борьбу чьей-то паранойей.

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

image



Исследование проблемы


Для начала вернемся (в который раз) к проблематике капчи. Усложнение капчи достигается путем добавления шумов и искажений. Но это палка о двух концах, ведь чем больше вы добавляете шумов и искажений, тем труднее человеку угадывать символы. Если ошибся с вводом — начинай все сначала. Часто просто вынужден обновлять по нескольку раз изображение, чтобы хоть как-то упростить себе задачу разгадывания.

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

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

Форма представляет собой набор именованых полей. Если просто заменить имена полей на абракадабру, то это ничего не даст, так как можно спокойно вычислить позицию элемента в дереве нод, сделать маппинг полей, отправить данные на сервер согласно маппингу. Проблему не решает, но развиваем мысль дальше. Пусть поля придут в случайном порядке с кодированными именами. Пользователь перед тем, как заполнять форму, сначала восстановит правильный порядок полей, и только потом заполняет форму и отправляет ее. Загадка данной капчи будет в том, чтобы получить правильный порядок полей. Как узнать, что порядок правильный? Можно промаркировать названия полей и сами поля ввода изображениями, цветами, фигурками, чем угодно, а потом задать задачу сложить паззл по какому-то алгоритму.

Алгоритм


Для начала создаем некий массив нормальных названий полей в эталонном порядке. Я буду пользоваться абстрактным языком для описания.

formData = [ { name: "first_name" }, { name: "last_name" }, { name: "nick" } ]

Добавляем в структуру хеши, которые сделаны на основании имени, плюс индекс, который пригодится позже.

formData = [
{
name: "first_name",
hash: "0947fh27",
index: 0
},
{
name: "last_name",
hash: "82jj2n4lE2",
index: 1
},
{
name: "nick",
hash: "jf12sjfjI9",
index: 2
}
]

Теперь создаем три обманных массива с неправильными позициями. Я упрощу это до индексов.

fake = [ [ 2, 1, 0 ], [ 2, 0, 1 ], [ 1, 0, 2 ] ]

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

fake = [ [ 2, 1, 0 ], [ 2, 0, 1 ], [ 0, 1, 2 ], [ 1, 0, 2 ] ]

Это все хорошо, но как теперь сделать паззл? Для демонстрации я выбрал простой паззл с геометрическими фигурками разных цветов. Нужно собрать их половинки. К нормальным данным я добавил одну половинку некой случайной фигурки, первому ложному массиву расставил пары согласно индексов.
formData = [
{
name: "first_name",
hash: "0947fh27",
index: 0,
picture: "red_circle_left"
},
{
name: "last_name",
hash: "82jj2n4lE2",
index: 1,
picture: "yellow_triangle_left"
},
{
name: "nick",
hash: "jf12sjfjI9",
index: 2,
picture: "green_circle_left"
}
]

fake[0] = [
{
hash: "jf12sjfjI9",
index: 2,
picture: "green_circle_right"
}
{
hash: "82jj2n4lE2",
index: 1,
picture: "yellow_triangle_right"
},
{
hash: "0947fh27",
index: 0,
picture: "red_circle_right"
}
]

Сохраняем эти структуры в сессию и начинаем формировать HTML часть. Для отрисовки графики я использовал css-классы, которым указал в качестве фона ссылки на картинки по индексу, а не по хешу, чтобы не было никакой подсказки для взломщика про истинный порядок.

Получилось примерно так:

.lp0 { background: url( /img/get/?type=l&pos=0) ... }
.lp1 { background: url( /img/get/?type=l&pos=1) ... }
.lp2 { background: url( /img/get/?type=l&pos=2) ... }
.rp0 { background: url( /img/get/?type=r&pos=0) ... }
.rp1 { background: url( /img/get/?type=r&pos=1) ... }
.rp2 { background: url( /img/get/?type=r&pos=2) ... }


Скрипт читает данные из сессии и выдает имена картинок для текстового описания полей (type=l) из нормального массива, а для самих полей (type=r) из массива-обманки.

Отрисовываем левую часть с текстовым описанием полей в нормальном порядке, а сами поля по массиву fake, при этом классы нодам расставляем в нормальном порядке как для левой, так и для правой части. Имена полей будут взяты из hash.

Все, дальше в ход вступает JS. Я использовал в качестве примера StateController, который отлично справился с задачей циклической перестановки полей и, в дальнейшем, задачей сбора данных.

После того, как пользователь «собрал» форму, он может ее заполнять.

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

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

Вуаля! Посмотреть демонстрацию функционала

Проблемы


Куда без них. Перечислю самые главные:
  • Сложность реализации немного выше, чем при обычной капче. Интеграция будет сложной для новичков.
  • Данные нельзя отправлять обычным POST или GET запросами, только JSON, дабы гарантировать позицию данных.
  • Не представляю как отправлять данные обычным способом, а не при помощи AJAX.
  • Не будет работать автозаполнение так как шифрованные имена полей.
  • Самое минимальное количество элементов форм — три. Дальше придется выкручиваться путем введения пустых полей-обманок

Методы улучшения защиты


Их можно придумывать долго, и основное направление, как мне видится, в создании разнообразных паззлов. Можно перебирать разные загадки, разные фигуры, разные принципы совмещения. Чтобы быстро не парсили текст загадки, можно сделать текст вопроса в виде картинки. Но это уже другая история…
Теги:
Хабы:
Всего голосов 72: ↑47 и ↓25+22
Комментарии102

Публикации

Ближайшие события