Избавление от KAPTCHA, раз и на всегда
Фразы «введите символы с картинки» начали всех уже не просто доставать, а их не читабельность, ну просто выводит из себя. Примерно с полгода назад я задумался а как можно обойтись без нее и все оказалось гораздо проще чем можно представить. Ну для начала посмотрим в историю, от куда появились «каптчи» и зачем — ответ очевиден, для того, что-бы избавиться от роботов разного уровня. Исходя из данного можно подумать теперь над другим: ботов пишут для сайтов типа форумы, блоги, т.е. тех куда можно в свободной и бесплатной форме запостить рекламное сообщение. Посмотрим на это со стороны «бото-писца», как пишут ботов?:
Можно понять, что сгенеренные поля или еще какие уловки хороший бот может отловить сразу, но все думают что каптчу то не сможет. В принципе «каптчу» отловить тяжело, для этого нужен либо алгоритм хеширования символов, но как правило используется не обратимый — такой метод не подходит, читать саму картинку — знавал человека который сделал сканер картинок, но не всем такое дано, использовать сервисы которые предлагают распознавать картинки — идеально. Вывод напрашивается сам: «каптча» не панацея, кому нужно, тот и ее пройдет, тогда зачем нам, разработчикам напрягать пользователей? Ну без защиты боты регаются и постятся в не малых количествах… Вот тут и появилась задача, для решения данной проблемы. Итак мы знаем что боты не умеют:
Исходя из такого громадного наличия инструментов, можно сделать свой алгоритм доступа. Не претендую стать первым и самым умным в данном вопросе, но пока что, данной технологии не встречал ни где, кроме своих сайтов и сайтов моих клиентов, разумеется. Начнем создавать «антибот» систему, в примере буду приводить выдержки кода с применением ZF и Doctrine
Итак, для начала мы создадим таблицу для временных записей, обзовем ее скажем так:
так же создадим файлик для AJAXa — fish.phtml, с содержанием
(«разумееться с проверкой в контроллере if ($this->_request->isXmlHttpRequest()){»):
Вернемся к форме регистрации или свободного поста, неважно к какой форме… но она должна иметь вот такую вставку:
ну и перейдя в саму форму добавим вот такое вот поле:
В принципе все, остался только AJAX обработчик, напишем и его:
Пошел наш субмит, поясню что происходит в посте на примере. Мы зашли на страницу с формой, послее ее загрузки у нас образовалась запись в базе:
unic_id = 44958
hash = 03cf63bea12bfcd3644846baa8481d07
mask = 4ca388cf
time_cr = 1285785784
ip = ххх.ххх.ххх.ххх
Далее мы записываем инпут с содержанием лишь одного ориентира:
После нажатия на кнопку «субмит» и обработки JS в форму были добавлены следующие параметры:
form action="/register/?crast=44958"
ну и соответственно это все послыается постом на адрес xxx/register/crast=44958, что же происходит в обработчике поста, рассмотрим:
В общем все, теперь смотрим у себя в проверках и выполняем нужные нам функции для регистрации, постинга и т.д.
Итоги: мы получили «защитника» от ботов, простым без «капчевым» путем.
Предугадываю некоторые нападения разъясню:
«Выполнять JS» — да это так, JS можно с эмулировать, но не факт что эмулированный JS выдаст нужные результаты
«Читать базу» — в нашем примере, JS служит лишь для замены и вызова параметров из таблицы, а они у нас генеряться фоном при загрузке, а т.к. их нет не на странице ни в заголовках, то их определить очень проблематично. То что вставка происходит на лету, этого бот отловить не сможет, а если и сможет, то тому мастеру который это сделает, ну просто не нужен будет Ваш форму или блог или еще что по проще, ему по зубам будет система безопасности банк-клиентов :-)
- Анализируют сайт
- Смотрят все формы и методы, куда и какие поля передаются
- Формируют агрегатор с использованием курлов либо сокетов
- Тестируют агрегатор на предмет отлавливания каких либо дополнительных полей
- Уже после этого запускают, наслаждаясь жизни
Можно понять, что сгенеренные поля или еще какие уловки хороший бот может отловить сразу, но все думают что каптчу то не сможет. В принципе «каптчу» отловить тяжело, для этого нужен либо алгоритм хеширования символов, но как правило используется не обратимый — такой метод не подходит, читать саму картинку — знавал человека который сделал сканер картинок, но не всем такое дано, использовать сервисы которые предлагают распознавать картинки — идеально. Вывод напрашивается сам: «каптча» не панацея, кому нужно, тот и ее пройдет, тогда зачем нам, разработчикам напрягать пользователей? Ну без защиты боты регаются и постятся в не малых количествах… Вот тут и появилась задача, для решения данной проблемы. Итак мы знаем что боты не умеют:
- Выполнять JS
- Формировать XmlHTTPRequest
- Читать базу — доступ до нее ни кто не даст
Исходя из такого громадного наличия инструментов, можно сделать свой алгоритм доступа. Не претендую стать первым и самым умным в данном вопросе, но пока что, данной технологии не встречал ни где, кроме своих сайтов и сайтов моих клиентов, разумеется. Начнем создавать «антибот» систему, в примере буду приводить выдержки кода с применением ZF и Doctrine
Итак, для начала мы создадим таблицу для временных записей, обзовем ее скажем так:
Antibot (
id - bigint(20)
unic_id - varchar(10)
hash - varchar(50)
mask - varchar(10))
time_cr - bigint(20)
ip - varchar(20)
);
так же создадим файлик для AJAXa — fish.phtml, с содержанием
(«разумееться с проверкой в контроллере if ($this->_request->isXmlHttpRequest()){»):
$tmp_data = Doctrine_Query::create()
->select('*')
->from('Antibot')
->where('unic_id = ?', $this->data)
->execute()
->toArray();
// эти данные нам нужны будут для вывода и замены данных в форме, об этом ниже
echo $tmp_data[0]['mask']."|||".$tmp_data[0]['hash']."|||gogo";
Вернемся к форме регистрации или свободного поста, неважно к какой форме… но она должна иметь вот такую вставку:
// ориентир, время, метка
$crast = dechex(time()+rand(00,99));
// Функция рендома символов
function random_simbols() {
/* тут может быть любой агрегатор рендомайзинга символов, зависит от вашей фантазии */
}
/* получаем ip посетителя, таким методом мы получим ip в любом случае и в любом это будет именно ip без проксей */
if (! function_exists('getenv')) {
$client_ip = $HTTP_SERVER_VARS['HTTP_CLIENT_IP'];
$x_frd_for = $HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR'];
$rmte_addr = $HTTP_SERVER_VARS['REMOTE_ADDR'];
} else {
$client_ip = getenv('HTTP_CLIENT_IP');
$x_frd_for = getenv('HTTP_X_FORWARDED_FOR');
$rmte_addr = getenv('REMOTE_ADDR');
}
if ($client_ip) {
$x_cip = explode(".", $client_ip);
$x_rmt = explode(".", $rmte_addr);
$ip = ($x_cip['0'] != $x_rmt['0']) ? implode(".", array_reverse($x_cip)) : $client_ip;
} elseif ($x_frd_for) {
$ip = (strstr($x_frd_for, ",")) ? end(explode(",", $x_frd_for)) : $x_frd_for;
} else {
$ip = $rmte_addr;
}
$mask = explode(".", $ip);
// создадим херню полную - извините за выражения, но оно так и есть
$unic_id = $mask[0] * ($mask[1] + $mask[2] + $mask[3] + rand(000,999));
// создадим хеш для сравнения, потом пригодиться
$hash = md5($unic_id."".random_simbols()."".$crast);
// создаем временную запись, в нашей антибот таблице
$thmdata = new Antibot();
$thmdata->unic_id = $unic_id;
$thmdata->hash = $hash;
$thmdata->mask = $crast;
$thmdata->time_cr = time();
$thmdata->ip = $ip;
$thmdata->save();
ну и перейдя в саму форму добавим вот такое вот поле:
<input type="hidden" value="<?=$unic_id?>" id="crast" />
В принципе все, остался только AJAX обработчик, напишем и его:
<script>
$(document).ready(function(){
// Повешаем событие на обработку кнопки субмита
$('#submit').click(function(){
doitnow='stop';
$.ajax({
type:"GET",
// Обратимся к нашему файлику, который создали вначале
url:"/index/fish/",
data:"data="+$('#crast').val(),
// Очень важная штука, без нее запрос будет выполняться не последовательно!
async:false,
// Если запрос прошел хорошо, т.е. AJAX выполнился действуем по нашим правилам
success:function(msg){
// Вот это, мы как раз и получаем из "фиша" и из полученных данных составляем свои
msg=msg.split("|||");
doitnow=msg[2];
// Помните, выше мы добавили в форму инпут, вот мы меняем ему имя и значения на лету, для минимилизации перехвата
$('#crast').attr({name:msg[0],value:msg[1]});
// Добавим к акшену формы дополнительный параметр
$("form").attr('action',$("form").attr('action')+'/?crast='+$('#crast').attr('name'));
// Закончили изваращаться над формой и элемента, пора в путь, жмем субмит
$("form").submit(function(){
if(doitnow=='gogo'){
return true;
}return false;
});
},error:function(){
alert('Произошла ошибка, нажмите "Вход в систему" еще раз');
return false;
}});
});
});
</script>
Пошел наш субмит, поясню что происходит в посте на примере. Мы зашли на страницу с формой, послее ее загрузки у нас образовалась запись в базе:
unic_id = 44958
hash = 03cf63bea12bfcd3644846baa8481d07
mask = 4ca388cf
time_cr = 1285785784
ip = ххх.ххх.ххх.ххх
Далее мы записываем инпут с содержанием лишь одного ориентира:
<input type="hidden" id="crast" value="44958">
После нажатия на кнопку «субмит» и обработки JS в форму были добавлены следующие параметры:
form action="/register/?crast=44958"
<input type="hidden" id="crast" name="4ca388cf" value="03cf63bea12bfcd3644846baa8481d07">
ну и соответственно это все послыается постом на адрес xxx/register/crast=44958, что же происходит в обработчике поста, рассмотрим:
// Если данный парам CRAST пустой значит пост искуственный
if ($_GET['crast'] == '') {
$error = "Вы наверное бот :-)";
// удалим нашу временную запись, что бы потом не смогли к ней обратиться
$tmp_data = Doctrine_Query::create()
->delete()
->from('Antibot')
->where('mask = ?', $_GET['crast'])
->execute();
} else {
// Возьмем данные из таблицы для сравнения
$tmp_data = Doctrine_Query::create()
->from('Antibot')
->where('mask = ?', $_GET['crast'])
->execute()
->toArray();
}
// Сравниваем полученные данные из таблицы и из поста, на их соответствие, вдруг их нагенерили свим способом, такое тоже возможно
if ($this->_request->getPost ($_GET['crast']) != $tmp_data[0]['hash']) {
// отловилась ошибка это робот
$error = "yes";
// удалим нашу временную запись, что бы потом не смогли к ней обратиться
$tmp_data = Doctrine_Query::create()
->delete()
->from('Antibot')
->where('mask = ?', $_GET['crast'])
->execute();
} else {
// скорее всего это человеческий пост
$error = "no";
// удалим нашу временную запись, что бы потом не смогли к ней обратиться
$tmp_data = Doctrine_Query::create()
->delete()
->from('Antibot')
->where('mask = ?', $_GET['crast'])
->execute();
}
В общем все, теперь смотрим у себя в проверках и выполняем нужные нам функции для регистрации, постинга и т.д.
Итоги: мы получили «защитника» от ботов, простым без «капчевым» путем.
Предугадываю некоторые нападения разъясню:
«Выполнять JS» — да это так, JS можно с эмулировать, но не факт что эмулированный JS выдаст нужные результаты
«Читать базу» — в нашем примере, JS служит лишь для замены и вызова параметров из таблицы, а они у нас генеряться фоном при загрузке, а т.к. их нет не на странице ни в заголовках, то их определить очень проблематично. То что вставка происходит на лету, этого бот отловить не сможет, а если и сможет, то тому мастеру который это сделает, ну просто не нужен будет Ваш форму или блог или еще что по проще, ему по зубам будет система безопасности банк-клиентов :-)
0 комментариев