Есть теория, что если долбить в php file_get_contents()'ом Гугло-Яндексы с редким интервалом, то они медленно, но верно, будут давать выдачу без запроса каптчи. Конечно это ничто. Но это примитивно, и со временем можно нарастить неплохую базу.
Я рискнул это проверить. Получился неплохой результат. Используем свободный хостинг с базой данных. В качестве объекта воздействия я неслучайно выбрал всеми обожаемые (в том числе и зоркомнадросом) "фильмы о любви". Да-да, те самые. Сентименты и романтика!
В процессе разработки основного скрипта потребовалось сохранять некоторые значения глобально. Выбрал способ писать конфиг в строчку таблицы базы через синглтон настроек. Пример класса приводить не буду. Все стандартно и неинтересно, но у кого есть желание - прошу в репу, где весь проект в открытом доступе.
Основная функция проекта - updateBase(...). Она принимает одну или несколько строк url с запросами к Яндекс или Google по заданной тематике блокировки. Она помещает в базу все адреса первой страницы поисковой выдачи. Для любовной лирики, это например:
<?php
updateBase(
'https://yandex.ru/video/search?from=tabbar&text=xxx',
'https://www.google.com/search?q=xxx&tbm=vid'
);
?>
Шаблон таблицы Blacklist'a (в дальнейшем - листа) следующий:
<?php
$query = "CREATE TABLE IF NOT EXISTS `meatlist` (
`ip` varchar(15) UNIQUE,
`url` varchar(100) NOT NULL DEFAULT '',
`last_active` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`ip`)
) DEFAULT CHARSET=utf8;";
?>
Поле last_active принимает время последнего обновления для данной записи. Время жизни записи ставлю ~полгода, при желании можно изменить:
<?php
$query = "DELETE FROM `meatlist`
WHERE (TO_SECONDS(CURRENT_TIMESTAMP())-TO_SECONDS(`last_active`)) > (180*24*60*60);";
?>
Время актуализации пишется в конфиг: в параметр last_update:
<?php
//пишем время подключения
$settings->set('lastUpdate', (string)time());
?>
Если скрипт запускается, а три дня после обновления базы еще не истекло, он не читает записи из базы, а открывает кэш последнего обновления и парсит его так, как если бы подключался снова. Это может быть полезно при наработке базы IP адресов, ведь gethostbyname() может давать целые серии разных IP при многократном использовании, что приятно для анализа.
<?php
//чтобы не попасть на капчу - вводим ограничение на частоту использования
if (!file_exists('result.txt') || time() - ($settings->get('lastUpdate') + 0) > 3*24*60*60) { //ставим период обновления три дня
$response = '';
//запрос через прокси
$context = stream_context_create(array(
'https' => array(
'proxy' => 'tcp://80.78.75.13:1080',//проверенный прокси
'request_fulluri' => true
),
));
//кидаем весь контент в кучу
foreach ($searchUrl as $url) $response .= file_get_contents($url, false, $context);
if (empty($response)) die('Bad proxy!'); //если триггер срабатывает значит пора менять прокси
//запись в result.txt
file_put_contents("result.txt", $response);
//пишем время подключения
$settings->set('lastUpdate', (string)time());
} else $response = file_get_contents('result.txt'); //чтение из файла
?>
Парсим регулярками:
<?php
//парсим все url без исключения
$pattern = "/https?:\/\/([\w\.-]{1,30}\.[\w]{2,30})/i";
preg_match_all($pattern, $response, $matches);
//отбираем исключения
$pattern="/(yandex\.ru|yastatic\.net|yandex\.net|youtube\.com|www\.w3\.org|vk\.com|vk\.cc|google|gstatic\.com|schema\.org)/i";
foreach ($matches[1] as $string) if (!preg_match($pattern, $string)) $blacklist[$string] = '';
?>
Как ни странно, в мясные списки попадают иногда ролики ВК и ok, как? Модерация интересная.
При обновлении базы пользуемся подготовленными запросами, чтобы слегка оптимизировать работу в цикле:
<?php
//добавляем в базу
$query = "INSERT INTO `meatlist` (`ip`, `url`)
VALUES (@ip := ?, @url := ?) ON DUPLICATE KEY
UPDATE `last_active` = CURRENT_TIMESTAMP(),`url` = @url;";
$stmt = $mysqli->prepare($query);
foreach($blacklist as $key => $value){
$ip = gethostbyname($key);
$stmt->bind_param('ss', $ip, $key);
$stmt->execute() or die('Ошибка запроса!');
}
$stmt->close();
?>
Вариантов работы скрипта несколько. Все зависит от параметра device:
при отсутствии параметра скрипт выдает список IP;
при ?device=mikrotik скрипт генерирует скрипт для Микротика;
при ?device=cisco скрипт выдает список правил для ACL;
Сама вилка:
<?php
$device=$_GET['device'] ?? NULL;
$outString = '';
switch ($device) {
case 'cisco':
$outString='access-list 30 deny ';
break;
case 'mikrotik':
giveRsc();
die();
}
//получение полного списка IP адресов для фильтра
$result = $mysqli->query("SELECT `ip`, `url` FROM `meatlist` ORDER BY `ip`;") or die(BASE_ERROR);
while ($row = $result->fetch_row()) echo $outString . "$row[0]" . '<br>';
if ($device == 'cisco') echo "access-list 30 permit any<br><br>";
?>
Практическая часть. Применение
Настройка блокировок в Mikrotik. Автоматическое обновление листа.
Дано: Mirkotik RB952Ui-5ac2nD
Задача: Настроить блокировку с автоматическим обновлением списка.
План действий: настраиваем правило файервола для блокировки по address list. Настраиваем планировщик на запуск скрипта с необходимым интервалом. В скрипте используем fetch для запроса к index.php?device=mikrotik и исполненяем результат запроса.
Решение 1. Графический интерфейс:
В WinBox заходим IP>Firewall, Вкладка Address Lists. Плюсуем лист с любым адресом: Name=blacklist Address:#Любой
Вкладка Filter rules. Плюсуем правило:
вкладка General: Chain=forward Protocol=0 In.Interface=#Местный_Бридж
вкладка Advanced: Dst.AddressList=#ВыбираемНашЛист
вкладка Action: drop
На вкладке Statistics можно сразу отслеживать результаты блокировки. При наличии пакетов естественно. Проверяем работу правила.
Далее System>Scheduler. Плюсуем задание:
В принципе в New schedule нас волнуют по большому счету только два поля: Interval и On Event.
Interval должен быть больше интервала обновления базы. Ставим 3d 00:01:00
On Event Кидаем наш скрипт. Знак ? в url запроса экранируем.
:tool fetch url="https://адрес.скрипта/index.php\?device=mikrotik" mode=https dst-path=BLscript.rsc;
:delay 2;
:import file-name=BLscript.rsc;
/file remove BLscript.rsc;
Задержка обязательна.
В начале можно выставить интервал в пять секунд для отладки. Смотрим лог - пошла жара:
Решение 2. Тоже самое, только тремя строчками через терминал. Коротко и ясно:
/ip firewall address-list/ add address=180.1.1.1 list=blacklist
/ip firewall filter add chain=forward protocol=0 in-interface=bridge-local dst-address-list=blacklist action=drop
/system scheduler add name=BLdownload interval=3d1m on-event=":tool fetch url=\"https://адрес.ресурса/index.php\?device=mikrotik\" mode=https dst-path=BLscript.rsc; :delay 2; :import file-name=BLscript.rsc; /file remove BLscript.rsc;"
Настройка access-list в маршрутизаторе Cisco. Автоматизированное обновление листа
Дано: Cisco SF-200-48
Задача: Ограничить доступ к ресурсам по заданному списку IP адресов.
Решение: Заходим на скрипт с любого браузера. Используем параметр ?device=cisco CTRL-A CTRL-C
Вставляем все это безобразие прямо в терминал маршрутизатора, в раздел config.
Для тестирования результатов применяем ACL. Заходим в раздел нужного интерфейса и прописываем:
ip access-group (номер нашего ACL) (out/in по назначению)
Как и все на свете данный способ далеко не идеален. Вот некоторые его недостатки:
При блокировке по IP бывает попадание в лист сети CDN. В результате оказываются заблокированными ресурсы вне заданных поисковых запросов;
Анти-каптча посредством "редких подключений" работает, но практически бесполезна. Подобные сайты ежедневно плодятся на хостингах, как насекомые и поисковики изобилуют выдачей новинок, практически каждую минуту. Нужно обновлять базу чаще. Нужно использовать разные поисковые запросы по теме и чередовать их;
В целом, если не заморачиваться, получился интересный бот. Хочу понаблюдать поведение на большом отрезке времени. Как часто обновлять прокси? Стоит ли автоматизировать? Как быстро запросы перейдут на каптчу? Время покажет.
Спасибо тем, кто разделяет интерес. Я верю, мы не одиноки.
nullc0de
Причем тут хаб CISCO и MikroTik?