В последнее время на фоне ИИ-бума прямо нашествие ботов на всех сайтах. Robots.txt стал не особо актуален; отсеивание по User Agent’у — тоже. Паразитный трафик вырос в десятки, а то и сотни раз. Буквально вчера добавлял правила у fail2ban для находящегося на публике RSS Bridge — через него повадились сканировать самые разные ТГ-каналы.
Так что достаточно вовремя я сделал кэширование и отдачу статики на своих вики-проектах, однако на сейчас уже и этого мало. Боты залазят на такие URL, куда живой человек вряд ли догадался бы зайти. Например, за 18 лет JabberWorld’а я практически никогда не пользовался разделом «Информация о странице», сейчас же в логах — тонны запросов с «action=info«. В итоге — да, для «живых людей» отдача статики, конечно, помогает, страничка будет отдаваться моментально, однако из-за хождения ботов по «особым» страницам все равно будет постоянно дергаться PHP, база и все, что с этим связано. Лишние затраты оперативной памяти, которую можно было выделить на что-то более полезное.
Пришлось принимать меры: порезать доступ на ряд страниц для незалогиненных пользователей — в чем-то не особо значимо, а в чем-то пришлось ущемить в возможностях.
В частности, пришлось закрыть доступ ко всем «Спецстраницам», кроме возможности авторизации и создания учетной записи, а также закрыть историю страниц (кроме «JID полезных jabber-ботов»). После первых блокировок выяснилось, что боты активно ходят по оставшимся ссылкам для логина на каждой из страниц и во всех возможных комбинациях параметров. Пришлось прятать такие ссылки, оставляя возможность логина только из боковой панели.
Итак, первое — .htaccess.
RewriteEngine On
RewriteBase /
#RewriteCond %{HTTP_HOST} ^www.jabberworld.info
#RewriteRule (.*) http://jabberworld.info/$1 [R=301,QSA,L,BCTLS]
RewriteCond %{REQUEST_URI} !^/cache/html/
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{HTTP_COOKIE} !UserID=
RewriteCond %{QUERY_STRING} !.
ReWriteCond %{THE_REQUEST} ^GET\x20/([^\x20/]+)\x20HTTP
RewriteCond %{DOCUMENT_ROOT}/cache/html/ns0\%3A%1.html.gz -s
RewriteRule ^(.+)$ cache/html/ns0\%253A%1.html.gz [B,L,NS,T=text/html]
<FilesMatch "\.html\.gz$">
# Сообщаем браузеру, что файл сжат через gzip
AddEncoding x-gzip .gz
ForceType text/html
Header append Vary Accept-Encoding
</FilesMatch>
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?title=$1 [QSA,BCTLS]
RewriteCond %{HTTP_COOKIE} !UserID=
# Проверяем, что в User-Agent есть слово "bot" (без учета регистра)
#RewriteCond %{HTTP_USER_AGENT} (bot|spider|crawl|slurp|mediapartners) [NC]
# Обращение к истории страницы (диапазоны вида /page/1234...4567)
RewriteCond %{REQUEST_URI} ^/[^/]+/(\d+)\D+(\d+)$
RewriteRule ^ - [F,L]
RewriteCond %{HTTP_COOKIE} !UserID=
# Проверяем, что в User-Agent есть слово "bot" (без учета регистра)
#RewriteCond %{HTTP_USER_AGENT} (bot|spider|crawl|slurp|mediapartners) [NC]
# Обращение к истории страницы с дополнительными параметрами
RewriteCond %{QUERY_STRING} ^title=[^&]*\/(\d+)(\.\.\.(\d+))?(&.*)?$
RewriteRule ^ - [F,L]
# Проверяем, что в User-Agent есть слово "bot" (без учета регистра)
RewriteCond %{HTTP_USER_AGENT} (bot|spider|crawl|slurp|mediapartners) [NC]
# Некорректные ссылки прежде всего от MJ12bot
RewriteCond %{REQUEST_URI} ^/.+/$
RewriteRule ^ - [F,L]
# 1. Проверяем, что пользователь НЕ авторизован (нет куки UserID)
RewriteCond %{HTTP_COOKIE} !UserID=
# 2. ИСКЛЮЧЕНИЯ: Разрешенные служебные страницы для гостей
# Исключения для REQUEST_URI (ЧПУ, проверяется чистый UTF-8 текст)
RewriteCond %{REQUEST_URI} !Служебная:Вход [NC]
RewriteCond %{REQUEST_URI} !Служебная:Выход [NC]
RewriteCond %{REQUEST_URI} !Служебная:Завершение_сеанса [NC]
RewriteCond %{REQUEST_URI} !Служебная:Создать_учётную_запись [NC]
RewriteCond %{REQUEST_URI} !Служебная:Отменить_подтверждение_адреса [NC]
RewriteCond %{REQUEST_URI} !Служебная:Сброс_пароля [NC]
RewriteCond %{REQUEST_URI} !Служебная:Требуемые_страницы [NC]
RewriteCond %{REQUEST_URI} !Служебная:Ссылки_сюда/ToDo [NC]
RewriteCond %{REQUEST_URI} !Special:UserLogin [NC]
RewriteCond %{REQUEST_URI} !Special:UserLogout [NC]
RewriteCond %{REQUEST_URI} !Special:CreateAccount [NC]
RewriteCond %{REQUEST_URI} !Special:InvalidateEmail [NC]
RewriteCond %{REQUEST_URI} !Special:PasswordReset [NC]
RewriteCond %{REQUEST_URI} !Special:WantedPages [NC]
RewriteCond %{REQUEST_URI} !Special:WhatLinksHere/ToDo [NC]
# Исключения для QUERY_STRING (старый формат ссылок, проверяется URL-код параметров)
RewriteCond %{QUERY_STRING} !UserLogin [NC]
RewriteCond %{QUERY_STRING} !UserLogout [NC]
RewriteCond %{QUERY_STRING} !CreateAccount [NC]
RewriteCond %{QUERY_STRING} !InvalidateEmail [NC]
RewriteCond %{QUERY_STRING} !PasswordReset [NC]
RewriteCond %{QUERY_STRING} !WantedPages [NC]
RewriteCond %{QUERY_STRING} !WhatLinksHere [NC]
RewriteCond %{QUERY_STRING} !type=signup [NC]
RewriteCond %{QUERY_STRING} !%D0%92%D1%85%D0%BE%D0%B4 [NC] # Вход
RewriteCond %{QUERY_STRING} !%D0%92%D1%8B%D1%85%D0%BE%D0%B4 [NC] # Выход
RewriteCond %{QUERY_STRING} !%D0%97%D0%B0%D0%B2%D0%B5%D1%80%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D1%81%D0%B5%D0%B0%D0%BD%D1%81%D0%B0 [NC]
# Создать_учётную_запись (закодировано)
RewriteCond %{QUERY_STRING} !%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C_%D1%83%D1%87%D1%91%D1%82%D0%BD%D1%83%D1%8E_%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D1%8C [NC]
# Отменить_подтверждение_адреса (закодировано)
RewriteCond %{QUERY_STRING} !%D0%9E%D1%82%D0%BC%D0%B5%D0%BD%D0%B8%D1%82%D1%8C_%D0%BF%D0%BE%D0%B4%D1%82%D0%B2%D0%B5%D1%80%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B0%D0%B4%D1%80%D0%B5%D1%81%D0%B0 [NC]
# Сброс_пароля (закодировано)
RewriteCond %{QUERY_STRING} !%D0%A1%D0%B1%D1%80%D0%BE%D1%81_%D0%BF%D0%B0%D1%80%D0%BE%D0%BB%D1%8F [NC]
# Требуемые_страницы (закодировано)
RewriteCond %{QUERY_STRING} !%D0%A2%D1%80%D0%B5%D0%B1%D1%83%D0%B5%D0%BC%D1%8B%D0%B5_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D1%8B [NC]
# Ссылки_сюда/ToDo (закодировано)
RewriteCond %{QUERY_STRING} !%D0%A1%D1%81%D1%8B%D0%BB%D0%BA%D0%B8_%D1%81%D1%8E%D0%B4%D0%B0/ToDo [NC]
# 3. БЛОКИРОВКА: Если это служебная страница, выдаем 403 ошибку
RewriteCond %{REQUEST_URI} (Служебная:|Special:) [NC,OR]
RewriteCond %{QUERY_STRING} (%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:|Special:) [NC]
# Блокируем доступ
RewriteRule ^ - [F,L]
# =====================================================================
# БЛОК 2: БЛОКИРОВКА ИСТОРИИ, DIFF И ИНФО ДЛЯ ГОСТЕЙ (ИСПРАВЛЕННЫЙ)
# =====================================================================
# 1. Проверяем, что пользователь НЕ авторизован
RewriteCond %{HTTP_COOKIE} !UserID=
# 2. ИСКЛЮЧЕНИЯ ДЛЯ СТРАНИЦ: Разрешаем параметры (включая returnto) для авторизации и Jabber-ботов
# Исключения для REQUEST_URI (ЧПУ)
#RewriteCond %{REQUEST_URI} !Служебная:Вход [NC]
RewriteCond %{REQUEST_URI} !Служебная:Выход [NC]
RewriteCond %{REQUEST_URI} !Служебная:Завершение_сеанса [NC]
#RewriteCond %{REQUEST_URI} !Служебная:Создать_учётную_запись [NC]
#RewriteCond %{REQUEST_URI} !Special:UserLogin [NC]
RewriteCond %{REQUEST_URI} !Special:UserLogout [NC]
#RewriteCond %{REQUEST_URI} !Special:CreateAccount [NC]
RewriteCond %{REQUEST_URI} !JID_полезных_Jabber-ботов [NC]
# Исключения для QUERY_STRING (включая URL-коды для index.php?title=...)
#RewriteCond %{QUERY_STRING} !UserLogin [NC]
RewriteCond %{QUERY_STRING} !UserLogout [NC]
#RewriteCond %{QUERY_STRING} !CreateAccount [NC]
RewriteCond %{QUERY_STRING} !type=signup [NC]
#RewriteCond %{QUERY_STRING} !%D0%92%D1%85%D0%BE%D0%B4 [NC]
# Вход
RewriteCond %{QUERY_STRING} !%D0%92%D1%8B%D1%85%D0%BE%D0%B4 [NC]
# Завершение сеанса
RewriteCond %{QUERY_STRING} !%D0%97%D0%B0%D0%B2%D0%B5%D1%80%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D1%81%D0%B5%D0%B0%D0%BD%D1%81%D0%B0 [NC]
# Создать учетную запись
#RewriteCond %{QUERY_STRING} !%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C_%D1%83%D1%87%D1%91%D1%82%D0%BD%D1%83%D1%8E_%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D1%8C [NC]
RewriteCond %{QUERY_STRING} !JID_%D0%BF%D0%BE%D0%BB%D0%B5%D0%B7%D0%BD%D1%8B%D1%85_Jabber-%D0%B1%D0%BE%D1%82%D0%BE%D0%B2 [NC]
# 3. БЛОКИРОВКА: Если это обычная страница и в запросе есть запрещенные параметры
RewriteCond %{QUERY_STRING} (diff=|action=history|oldid=|returnto=|action=info|undo=|undoafter=|returntoquery=) [NC]
RewriteRule ^ - [F,L]
# Проверяем, что в User-Agent есть слово "bot" (без учета регистра)
RewriteCond %{HTTP_USER_AGENT} (bot|spider|crawl|slurp|mediapartners) [NC]
# Проверяем наличие "action=edit" ИЛИ "diff=", а также обращение к истории в строке запроса
RewriteCond %{QUERY_STRING} (action=edit|diff=|action=history|returnto=|oldid=|action=info|undo=|undoafter=|returntoquery=) [NC]
# Блокируем запрос с кодом 403 Forbidden
RewriteRule ^ - [F,L]
Тут в начале блок по отдаче статики, блок для «красивых» ссылок, а дальше начинаются блокировки «неугодных». Если вначале обходился перечислением вариантов ботов, то на сейчас все сместилось в сторону «!UserID=» — т.е., незалогиненный пользователь.
Маленькие блоки сделал еще до массовых запретов. Там прежде всего идет блокировка от обращений к истории страницы — URL’ы вида
/страница/1234...3454/страница/1234title=страница/1234?параметры
У MJ12bot какой-то корявый парсер и я получал много запросов со слэшем в конце страницы. Заблокировал тоже.
А дальше — блокировка спецстраниц, кроме того, что связано с логином и некоторых вспомогательных. Активно писалось с помощью гуглоИИ (да, «какие-то машины нас убивают, какие-то помогают жить» — или как там в Матрице было?). Честно говоря, так и не вник, можно ли было с помощью какой-то логики слить этот и следующий блок, но ИИ убеждал, что лучше сделать 2 отдельных. А в следующем делается блокировка для гостей по обращению к страницам с передачей параметров — и да, в частности уже после попрятанных ссылок на логин убрал из разрешенных страницы входа с параметром (до того — было много запросов от ботов вида «title=Служебная:Вход?returnto=какая-то_страница«). Сейчас вход делается только строго в одном месте — из боковой панели, а там уже никаких параметров не передается.
Последний блок htaccess’а — блокировка доступа к истории и сервисным страницам для «хороших» ботов, которые не маскируются под обычных пользователей. Правило осталось со «старых» времен — сейчас не знаю, насколько оно актуально. Оставил.
Второе. Ссылка на вход. Открываем Mediawiki:Sidebar и в удобном для себя блоке делаем ссылку на Служебная:UserLogin. Например, у меня это обычно выглядит так:
* Участникам
** Служебная:UserLogin|Авторизоваться
Третье. Для анонимных посетителей убираем в верхней части страницы ссылки на «Войти», «Создать учетную запись» и так далее. В LocalSettings.php добавляем в конец:
$wgHooks['SkinTemplateNavigation::Universal'][] = function ( $skin, &$links ) {
// Проверяем, вошел ли пользователь в систему
$user = $skin->getUser();
// Если пользователь НЕ зарегистрирован (гость)
if ( !$user->isRegistered() ) {
$elements_to_remove = [
'createaccount', // Создать учётную запись
'login', // Войти
'anontalk', // Обсуждение (для анонимов)
'anoncontribs' // Вклад (для анонимов)
];
foreach ( $elements_to_remove as $key ) {
// Удаляем из выпадающего меню
if ( isset( $links['user-menu'][$key] ) ) {
unset( $links['user-menu'][$key] );
}
// Удаляем из стандартной панели
if ( isset( $links['personal'][$key] ) ) {
unset( $links['personal'][$key] );
}
}
}
return true;
};
Дополнительно можем убрать вверху надпись «Вы не авторизованы». Для этого открываем MediaWiki:Notloggedin и убираем оттуда текст.
Не знаю, почему, но иконка человечка на JabberWorld не убирается, а на linuxoid.in ее нет (хотя я не делал никаких действий, чтобы ее не было).
Четвертое. При анонимных правках (там, где они разрешены) в верхней части светится баннер «Вы не вошли в систему...«. Из него есть ссылки на создание учетной записи и вход с параметрами для возврата к редактируемой странице. Боты этим активно пользуются и ходят по этим ссылкам. Пришлось переделать. Обычным пользователям может быть будет чуть-чуть не так удобно, но зато упрощает блокировки для «необычных». Открываем MediaWiki:Notloggedin, правим там текст до такого вида:
<strong>Внимание:</strong> Вы не вошли в систему. Ваш IP-адрес будет общедоступен, если вы запишете какие-либо изменения. Если вы <strong>[[Служебная:Вход|войдёте]]</strong> или <strong>[[Служебная:Создать_учётную_запись|создадите учётную запись]]</strong>, её имя будет использоваться вместо IP-адреса, наряду с другими преимуществами.
Так ссылки будут работать, но будут вести только лишь на соответствующие страницы, без возврата на редактируемую.
На пока все, нагрузка уже ощутимо снизилась; будут обновления — дополню.