
Автоматизируйте блокировку доступа вредоносных ботов к вашему серверу
TUI, веб-интерфейс и CLI, которые помогают настроить сервер для блокировки нежелательных ботов без необходимости прятаться за CDN.
Он работает вместе с NGINX и вашим существующим межсетевым экраном (iptables или nftables), на двух отдельных уровнях:
Приложение изо всех сил старается не заблокировать вам доступ к серверу, но вы используете его на свой страх и риск. И учтите, что оно лицензировано под AGPL, поэтому если вы используете его коммерчески, убедитесь, что соблюдаете букву лицензии.
Из crates.io:``` cargo install stop-bots
Или соберите из рабочей копии:```
cargo install --path .
Предварительно собранный бинарник x86_64 для Linux доступен на GitHub в разделе releases.
Для сборки требуется Rust 1.88 или новее. На практике только Linux: программа вызывает
systemctl, nginx -t и nft/iptables, так что хотя она и компилируется на других
платформах, пользы там от неё будет немного.
Запустите бинарник без аргументов, чтобы открыть TUI, stop-bots web — для тех же экранов
в браузере (см. Веб-интерфейс), или stop-bots --help — для полного списка
подкоманд CLI. TUI и CLI можно использовать вместе. Настройте всё в TUI, а затем
используйте CLI в crontab, чтобы поддерживать правила в актуальном состоянии.
Выйти из приложения или закрыть всплывающее окно/подменю можно клавишей 'q' или Escape.
Переключаться между тёмной и светлой темой можно клавишей 't'. Приложение попытается автоматически определить тему, но для некоторых комбинаций терминала и мультиплексора доступной информации недостаточно, чтобы сделать правильный выбор.
1–4 (или d/b/s/p) переходят сразу к нужному экрану; Left/Right или их
vim-алиасы h/l переключают их по порядку; ? в любой момент открывает полный
справочник по привязкам клавиш, а : открывает палитру команд со списком всех действий
по названию. Tab / Shift+Tab всегда перемещают между панелями текущего экрана, никогда
между экранами.
Dashboard владеет всем, что попадает в firewall script; Site settings владеет всем, что попадает в NGINX config. Это разделение определяет, где живёт та или иная настройка.
Up/Down перемещают между тремя списками; m переключает режим геоблокировки. Нажмите F,
чтобы отрендерить текущие правила firewall в скрипт — во всплывающем окне также есть
переключатель "apply after writing" (Space) для немедленного применения, вместо применения
вручную afterward. Ещё три клавиши действуют на весь хост: u скачивает все списки,
a применяет обе плоскости (NGINX, затем firewall), а w ставит эту консоль за NGINX —
те же три, что в браузере представлены кнопками и панелью.Tab для фокуса на ней), содержащая
общехостовые настройки, формирующие генерируемый конфиг — что получает заблокированный
запрос в ответ (см. ниже), нужно ли отдавать сгенерированный robots.txt, и ограничение
скорости — над каждым сайтом NGINX, обнаруженным на диске, каждый со статусом
"up to date / stale / not found" в реальном времени и действиями для применения текущей
политики к одному сайту или ко всем сразу. Изменение любой из этих настроек переводит
каждый применённый сайт в STALE, что служит сигналом к повторному применению. Открытие
сайта позволяет переопределить его политику категорий/ботов, включить любое из шести
правил формы запроса и перечислить пути, исключённые из блокировки.NOT BLOCKED/BLOCKED (показано красным). Tab/Shift+Tab переключают, к какой
из двух панелей применяются Up/Down; f циклически переключает общий фильтр
(all / not blocked only / blocked only); Enter блокирует выбранную строку NOT BLOCKED
или разблокирует её, если она уже BLOCKED. i инспектирует выбранный адрес: какие из
репутационных фидов его содержат, находится ли он внутри опубликованного диапазона
краулера (что и отличает настоящий Googlebot от user agent, который лишь заявляет себя
таковым), к какой стране он принадлежит и под какими учётными записями он пытался войти.
Всё это из списков, которые хост уже скачал — здесь нет обратного DNS или whois-запроса,
потому что PTR-запись пишется тем, кто владеет адресом, и была бы предоставленным
атакующим текстом, читающимся как авторитетный.Bot settings, где живёт каждый источник списков и каждый отдельный бот:
Site settings, где общехостовые настройки NGINX расположены над каждым сайтом, найденным на диске:
Dynamic Protection, живое представление того, что атакует сервер прямо сейчас:
Известные боты, по категориям (scanner / search engine / AI crawler), из
ArcJet's Well-Known Bots,
ai.robots.txt и списка
NGINX Ultimate Bad Bot Blocker.
Блокировка категории внедряет правило if ($http_user_agent ...) в конфиг NGINX каждого
сайта (apply-blocks / a/A в Site settings).
Слишком много запросов, через собственное ограничение скорости NGINX. В отличие от всего остального здесь, это применяется NGINX во время запроса, а не путём анализа лога после. По умолчанию выключено: лимит, настроенный под неправильный сайт, отваживает реальных посетителей.
Вежливо, сначала — опционально генерируемый robots.txt со списком всех ботов,
которых вы блокируете, для краулеров, которые его соблюдают, плюс путь-приманка ниже.
По умолчанию выключено, потому что он заменяет то, что ваш сайт отдаёт по /robots.txt
сегодня.
Кроме случаев, когда вы указали иначе — исключения путей для каждого сайта, чтобы
можно было блокировать AI-краулеров везде, кроме /blog.
Запросы, не похожие на браузер, для каждого сайта. Шесть независимых правил, каждое со своим переключателем и каждое по умолчанию выключено — по одному переключателю на правило, чтобы если что-то ваше перестанет работать, вы могли определить, какое правило это сделало:
Ко всем ним применяются две меры предосторожности, и они принудительно соблюдаются, а не оставлены на вас:
server. Браузеры
не используют HTTP/2 без TLS, поэтому на простом блоке listen 80 каждый запрос —
HTTP/1.1, включая редирект, который браузер делает на пути к HTTPS. Ваши блоки для
порта 80 и порта 443 обычно делят server_name, поэтому настройка достигает обоих;
только TLS-блок получает эти правила. Правила формы заголовков работают поверх простого
HTTP и записываются в оба./.well-known/ всегда исключается, как только включено любое правило. Именно там
Let's Encrypt забирает свой HTTP-01 challenge, по HTTP/1.1 без Accept и часто без
User-Agent — без этого исключения ваш сертификат перестанет обновляться через
несколько недель.Один общехостовый выбор в Site settings. Это не взаимозаменяемые коды состояния — каждый говорит что-то своё, и разница важнее всего для клиентов, которых вы не хотели поймать:
| Опция | Для чего она |
|---|---|
403 Forbidden (по умолчанию) | говорит, что блокировка была намеренной; единственная, на которую ошибочно пойманный человек может отреагировать |
404 Not Found | скрывает, что что-либо вообще было заблокировано |
410 Gone | просит благовоспитанных краулеров удалить URL навсегда — предпочитайте это 403, когда вы отваживаете краулеров, а не атакующих |
429 Too Many Requests | говорит вежливому клиенту отступить и повторить попытку |
418 I'm a teapot | шутка из RFC 2324. Работает; просто не зарегистрирована в IANA, и NGINX отправляет её с пустым телом |
444 close connection | вообще без ответа; самый дешёвый, но неотличим от недоступного сервера |
Tarpit | отвечает 403, но выдаёт тело по одному байту в секунду, так что клиент ждёт, вместо того чтобы двигаться дальше |
Tarpit — самый мягкий вариант при ложном срабатывании: ошибочно пойманный клиент
замедляется, а не отклоняется — и самый жёсткий по стоимости для бота, чьё соединение
простаивает. Два момента, которые нужно знать перед его выбором: он удерживает одно из
ваших worker-соединений на всё это время тоже, так что поток tarpit-клиентов конкурирует
с реальными посетителями за worker_connections; и то, как долго он на самом деле длится,
зависит от того, как NGINX решит записать небольшое тело ошибки, что отмечено в TODO.md
как требующее проверки на реальном сервере.
Каждый из них — независимый переключатель на панели "Automatic blocking" в Dashboard, и каждый добавляет временную блокировку firewall, которая истекает сама и добавляется заново, если поведение продолжается.
Они работают по внутреннему таймеру, который перечитывает ваши SSH- и NGINX-логи доступа каждую минуту — но только пока запущен TUI или веб-интерфейс. Любой из них поддерживает то же расписание, в той же базе данных, так что оставить веб-интерфейс запущенным достаточно; ничего не обнаруживается, когда не запущен ни один. Для сервера, на котором вообще нет процесса stop-bots, см. Unattended, from cron ниже.
/.env, /.git/config, /wp-config.php и
подобным сам по себе является решающим, так что порог здесь не нужен. Встроенный список
намеренно опускает пути, которые где-то легитимны — /wp-login.php, /wp-admin/,
/xmlrpc.php, /phpmyadmin — поскольку заблокировать собственного администратора было
бы хуже, чем пропустить сканер, которого детектор 404 всё равно поймает. Добавляйте свои
через set-probe-paths.Disallow: в сгенерированном robots.txt
и нигде не связанный. Достичь его означает проигнорировать robots.txt, чего ничто
легитимное не делает случайно — самый сильный сигнал здесь и самая длинная блокировка.
Для работы требует включённой генерации robots.txt.Ещё три смотрят на то, как клиент ведёт себя, а не на то, что он запрашивает. Все три по умолчанию выключены, потому что у каждого есть ложное срабатывание, которое он не может исключить сам по себе — и все три исключают проверенных краулеров поисковых систем, которые иначе совпали бы с каждым из них:
304 считается загруженным ресурсом). Не
поможет на сайте, который вообще не отдаёт ресурсов — чистом JSON API.Referer. Ослаблено
Referrer-Policy: no-referrer и инструментами приватности; порог по различным путям —
это то, что вообще делает его пригодным./24 помечены в
одном проходе, блокируется /24. По умолчанию выключено — блокировка 256 адресов из-за
трёх плохо себя ведущих — это сопутствующий ущерб по замыслу. (IPv6 отличается и не
требует переключателя: обнаружение всегда блокирует /64, потому что /64 — это одна
LAN, то же самое, что представляет один IPv4-адрес. Блокировка единственного адреса,
который случилось использовать IPv6-атакующему, ничего бы не остановила — у него есть
ещё 2^64.)Всё вышеперечисленное — сгенерировано. В силе ли что-либо из этого — отдельный вопрос,
и stop-bots status — тот, кто на него отвечает:```
stop-bots status
Семь проверок, и первая — та, ради которой всё это стоит делать: действительно ли сгенерированные правила находятся в ядре, или только на диске? Реальный хост проработал три недели с 48 860 правилами drop в `/etc/stop-bots/firewall.nft` и пустым набором правил, потому что написание скрипта и его загрузка — это два шага, и никто никогда не смотрел на второй.
Остальные: переживёт ли набор правил перезагрузку (включён ли `nftables.service`?), соответствует ли скрипт по-прежнему правилам, применены ли блоки NGINX, запускает ли консольная служба тот бинарник, который она называет, есть ли место для базы данных и могут ли детекторы читать свои логи.
Он завершается с ненулевым кодом, если что-либо **CRITICAL**, поэтому работает как проверка мониторинга. `--quiet` выводит только то, что требует внимания, — это форма для cron:```
0 * * * * /usr/local/bin/stop-bots status --quiet
Проверка, которая не смогла выполниться — nft list требует root — сообщает UNKNOWN, а не
OK. Проверка состояния, которая говорит, что всё в порядке, потому что не смогла посмотреть, хуже,
чем её отсутствие, потому что ей верят.
Тот же отчёт есть на Dashboard как в консоли, так и в TUI, снимается ежечасно внутренним cron,
а не при каждом рендере: nft list на большом наборе правил — это мегабайты текста.
Каждое решение по фаерволу выше генерируется, но никогда не применяется автоматически: render-firewall
(или клавиша f на Dashboard) записывает скрипт iptables или nftables, чтобы вы сами его просмотрели и применили,
и отказывается записывать такой, который заблокировал бы текущую подключённую SSH-сессию.
Применить его за вас могут три вещи, и все три требуют вашего запроса: попап рендера в TUI
("apply after writing") или его клавиша a, панель фаервола веб-консоли ("run it after
writing") или её кнопка "Apply everything", и batch --apply из crontab, который написали вы — см.
Unattended, from cron. Ни одно из них не является побочным эффектом чего-либо
автоматического: внутренний cron рендерит скрипт и никогда его не запускает.
То же самое касается стороны NGINX: изменение настройки меняет лишь то, что было бы записано.
Настройки сайтов показывают каждый сайт как STALE, пока вы не примените.
Отключение детектора никогда не удаляет блокировки, которые он уже добавил — они истекают сами.
"Stop detecting" и "undo what was detected" намеренно разделены; второе — это
экран Dynamic Protection или remove-firewall-rule.
Есть также простой подсчёт по access-логам, независимый от блокировки: record-access-stats /
list-access-stats считают, как часто каждый user agent появляется в успешных (не ошибочных)
запросах, так что вы видите, кто на самом деле посещает сайт, помимо того, кто блокируется.
stop-bots batch — это один проход по всему, что TUI делает вручную: обновить каждый список,
просканировать логи, записать правила блокировки NGINX и скрипт фаервола.```
0 4 * * * root /usr/local/bin/stop-bots batch --apply --ssh-log /var/log/auth.log
*/10 * * * * root /usr/local/bin/stop-bots batch --apply --no-fetch --ssh-log /var/log/auth.log
Он ничего не сообщает, когда всё сработало, поэтому успешный ночной запуск не отправляет вам письмо. Неудачный
шаг выводит сообщение в stderr и устанавливает ненулевой код завершения — именно поэтому cron сообщает вам
об этом. Сначала запустите его вручную с `--verbose` — это выводит по строке на каждый шаг и является
самым простым способом увидеть, что он на самом деле делает.
**Именно `--apply` заставляет его что-либо применять.** Без него `batch` записывает конфигурацию NGINX
и скрипт брандмауэра и останавливается: конфигурация ничего не делает до перезагрузки, скрипт ничего не делает
до его запуска. Это поведение по умолчанию для этого проекта везде, и здесь оно остаётся поведением по умолчанию.
`batch` и долго работающий фронтенд безопасно сосуществуют. TUI, веб-интерфейс и `batch` —
все записывают то, что они сделали, через одни и те же ключи в одной и той же базе данных, поэтому кто бы ни добрался до задачи
первым, выполняет её, а остальные обнаруживают, что она больше не подлежит выполнению — вы не получаете двух проходов обнаружения, и
панель «Запланированные задачи» на Dashboard показывает то, что действительно произошло, а не утверждает,
что всё просрочено. Если вы уже держите веб-интерфейс запущенным, ночная запись `batch` — это дополнительная подстраховка,
а не необходимость; если нет, это единственное, что поддерживает
обнаружение в актуальном состоянии.
**С `--apply` защита от блокировки SSH может отказать — а отказ означает, что ничего не применяется.**
Она отказывает, если правила заблокировали бы клиента, подключённого прямо сейчас, *и* если ни один журнал SSH
вообще не удалось прочитать, потому что тогда проверка не могла выполниться. Интерактивный
`render-firewall` в этом втором случае лишь выводит примечание, исходя из того, что за терминалом наблюдает человек;
из cron никто не наблюдает. **Передавайте `--ssh-log` явно**: cron работает от имени root, поэтому `/var/log/auth.log` обычно читается
нормально, но на хосте только с journald `journalctl` под cron может вернуть пустой результат — и это ровно тот случай, в котором она отказывает. `--force` переопределяет
защиту, если вы действительно этого хотите.
Отказ одного шага никогда не останавливает остальные, и половины NGINX и брандмауэра независимы —
неудачная перезагрузка NGINX всё равно оставляет брандмауэр применённым, и наоборот.
`batch` записывает каждый шаг в соответствии с тем же расписанием, которое использует внутренний cron TUI, поэтому они
согласуются насчёт того, что уже было запущено, вместо того чтобы делать это обоим, и панель «Запланированные
задачи» на Dashboard показывает то, что сделал ваш настоящий cron.
# Веб-интерфейс
`stop-bots web` обслуживает те же пять экранов в браузере.```
stop-bots web
Он привязывается к 127.0.0.1:8787 — доступен только с этой машины — и выводит сгенерированный пароль один раз, при первом запуске. Получите к нему доступ со своего ноутбука через SSH-туннель:``` ssh -L 8787:127.0.0.1:8787 your-server
затем откройте <http://127.0.0.1:8787/>.

Консоль следует светлой или тёмной теме операционной системы, с переключателем в
заголовке; приведённые выше скриншоты TUI — это тёмная тема, а эти — светлая. Клавиши,
которые использует TUI, работают и здесь: `1`–`4` переключают экраны, `/` переводит фокус на поле поиска, `?` открывает справку.

Три действия для всего хоста находятся в заголовке — в браузере в виде кнопок, а в TUI
в виде отдельных клавиш:
- **Обновить всё** (`u`) загружает каждый список ботов, каждый диапазон IP сканеров, каждый
*включённый* фид репутации и каждую *выбранную* страну — тот же набор, который загружает `stop-bots batch`, по тому же плану. Сбой одного источника не останавливает остальные, и ничего не применяется, пока что-то это не применит.
- **Применить всё** (`a`) записывает и перезагружает конфигурацию NGINX, затем записывает и запускает
скрипт межсетевого экрана. Эти два уровня независимы: что бы ни отказало, другой всё равно получит
свой ход, потому что наполовину применённый хост лучше, чем тот, где синтаксическая ошибка NGINX также оставила
межсетевой экран устаревшим.
- **Веб-доступ** (`w`) настраивает NGINX для обслуживания самой консоли — см.
[За NGINX](#behind-nginx-a-subdomain-or-a-path-prefix).
## Как служба (Debian)```
sudo stop-bots install web
Записывает /etc/systemd/system/stop-bots-web.service, создаёт /var/lib/stop-bots (0700 — там
хранится хеш пароля консоли) и /etc/stop-bots, генерирует пароль, если его нет,
и включает и запускает юнит.
--dry-run выводит весь план и ничего не меняет. Это единственная команда в
проекте, которая запускает демон, так что начните с неё. --prefix <dir> записывает то же дерево куда-нибудь,
где вы можете прочитать его без root. Если юнит уже существует и вы его отредактировали, установщик
останавливается и сообщает об этом, вместо того чтобы заменить вашу правку; --force, если вы это имели в виду.
Служба работает от имени root, потому что консоль перезаписывает /etc/nginx, записывает скрипт
брандмауэра и запускает nginx -t и systemctl reload nginx. Нет непривилегированного разделения, которое
сохранило бы набор функций нетронутым. Юнит несёт в себе ту защиту, которая выживает при этом требовании,
и комментарий, объясняющий, какая защита была опущена и почему.
Адрес привязки, список разрешённых хостов и префикс пути намеренно не указаны в юните —
работающий сервер перечитывает их из базы данных, поэтому их размещение в ExecStart дало бы
им два источника истины. Изменяйте их с помощью stop-bots web --save ... и перезапускайте.
Одна вещь меняется, когда это запускается от имени root: ежедневная задача RenderFirewall внутреннего
cron теперь может записывать /etc/stop-bots/firewall.nft, чего она не могла, когда вы запускали консоль вручную
от своего имени. Ничто не применяет этот скрипт — его запуск по-прежнему остаётся за вами.
Проверяется только Debian, потому что именно он был протестирован; юнит, скорее всего, корректен на любом дистрибутиве с systemd, но путь к журналу SSH, который он предполагает, — дебиановский.
Привязка к чему-либо, кроме loopback, требует второго, намеренного флага, потому что эта консоль может перезаписать брандмауэр и конфигурацию NGINX хоста, на котором она работает:``` stop-bots web --bind 0.0.0.0:8787 --expose --allowed-hosts admin.example.com --save
`--allowed-hosts` на практике не является необязательным: запрос с именем хоста, которого нет
в списке, отклоняется. Именно это делает атаку DNS rebinding на консоль невозможной, и именно
поэтому открытый сервер, к которому обращаются по имени, требует явного указания этого имени.
Разместите его за NGINX с TLS — тем же NGINX, который защищает этот инструмент. Если вы так
сделаете и прокси устанавливает `X-Forwarded-For`, сообщите консоли, что она может доверять этому
заголовку, иначе она не сможет определить, с какого адреса на самом деле пришёл запрос:```
stop-bots web --bind 127.0.0.1:8787 # and set web:trust_forwarded_for
За TLS также установите web:secure_cookie. Без него браузер будет отправлять сессионный
cookie и на http:// URL для того же хоста.
web:trust_forwarded_for важнее, чем кажется. Без него каждый запрос за
прокси приходит с 127.0.0.1, поэтому консоль не может отличить одного клиента от другого — а
это значит, что поток попыток входа делит одно и то же ведро throttle с вами, и защита, которая
не даёт вам заблокировать собственный адрес, не с чем сравнивать. С ним и то, и другое работает
для каждого клиента отдельно.
Консоль может настроить это за вас, как и TUI (w на Dashboard). Оба
записывают конфигурацию NGINX, сохраняют префикс пути и добавляют имя хоста в allowlist —
три вещи, которые должны согласовываться, потому что отсутствующий префикс заставляет каждую ссылку покидать
блок location, а отсутствующее имя хоста делает каждый запрос 403. Оба проверяют с помощью
nginx -t перед тем, как конфигурация вступит в силу, откатывают её при неудаче и записывают
новый адрес только после успешной проверки.
Два режима, и path является режимом по умолчанию не просто так: он добавляет блок location
к сайту, который у вас уже есть, поэтому консоль наследует сертификат этого сайта. Поддомену нужен собственный,
и пока не выполнен certbot --nginx -d <host>, форма пароля этой консоли и сессионный
cookie передаются по сети в открытом виде.
Остальная часть этого раздела — то же самое вручную, и это стоит прочитать один раз, даже если вы используете панель — ловушка с завершающим слешем ниже и есть та ошибка, для предотвращения которой она существует.
Поддомен — более простое развёртывание, и его стоит выбрать, если у вас есть такая возможность:```nginx server { server_name stopbots.example.com; location / { proxy_pass http://127.0.0.1:8787; proxy_set_header Host $host; } }
```yaml
- name: "Test"
uses: actions/checkout@v4
Создайте файл config.yaml:
# Конфигурация сканера
scanner:
# Целевые URL для сканирования
targets:
- "https://example.com"
- "https://test.example.com"
# Настройки сканирования
settings:
timeout: 30
threads: 10
user_agent: "SecurityScanner/1.0"
# Модули для включения
modules:
- xss
- sqli
- lfi
- rce
# Настройки вывода
output:
format: "json"
file: "scan_results.json"
verbose: true
# Базовое сканирование
python scanner.py --config config.yaml
# Сканирование с переопределением параметров
python scanner.py --url https://target.com --modules xss,sqli --output results.json
# Сканирование с аутентификацией
python scanner.py --url https://target.com --auth-token "your_token_here"
Создайте собственные полезные нагрузки в директории payloads/:
# payloads/custom_xss.py
XSS_PAYLOADS = [
"<script>alert('XSS')</script>",
"",
"<svg onload=alert('XSS')>",
# Добавьте свои полезные нагрузки здесь
]
Пример интеграции с GitHub Actions:
name: Security Scan
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: |
pip install -r requirements.txt
- name: Run security scan
run: |
python scanner.py --config config.yaml --output results.json
- name: Upload results
uses: actions/upload-artifact@v3
with:
name: scan-results
path: results.json
from scanner import Scanner
# Инициализация сканера
scanner = Scanner(
target="https://example.com",
modules=["xss", "sqli"],
threads=10,
timeout=30
)
# Запуск сканирования
results = scanner.scan()
# Обработка результатов
for vulnerability in results.vulnerabilities:
print(f"Найдено: {vulnerability.type}")
print(f"URL: {vulnerability.url}")
print(f"Серьёзность: {vulnerability.severity}")
| Метод | Описание | Параметры |
|---|---|---|
scan() | Запуск полного сканирования | Нет |
scan_url(url) | Сканирование одного URL | url (str) |
add_module(module) | Добавление модуля сканирования | module (str) |
set_auth(token) | Установка токена аутентификации | token (str) |
export(format) | Экспорт результатов | format (str) |
Проблема: Сканирование зависает или не завершается
Решение: Увеличьте значение таймаута и уменьшите количество потоков:
python scanner.py --timeout 60 --threads 5
Проблема: Ошибки SSL-сертификата
Решение: Используйте флаг --insecure для пропуска проверки SSL:
python scanner.py --url https://target.com --insecure
Проблема: Ложноположительные срабатывания
Решение: Настройте уровни серьёзности и исключите определённые шаблоны:
scanner:
settings:
min_severity: "medium"
exclude_patterns:
- "*.css"
- "*.js"
- "/static/*"
Мы приветствуем вклад в развитие проекта! Пожалуйста, следуйте этим рекомендациям:
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)Этот проект лицензирован под лицензией MIT — подробности см. в файле LICENSE.
Этот инструмент предназначен только для законного тестирования на проникновение и оценки безопасности. Пользователи несут ответственность за соблюдение всех применимых законов и получение надлежащего разрешения перед сканированием любых систем. Авторы не несут ответственности за любое неправомерное использование или ущерб, причинённый этим программным обеспечением.``` stop-bots web --allowed-hosts stopbots.example.com --save
**Префикс пути тоже работает**, но консоли нужно об этом сообщить — она должна
генерировать каждую ссылку, действие формы, перенаправление и путь cookie уже с
префиксом, и она не может догадаться:```
stop-bots web --base-path /stop-bots --allowed-hosts example.com --save
| --proxy | Прокси-сервер для использования (например, http://127.0.0.1:8080) |
| --timeout | Тайм-аут запроса в секундах (по умолчанию: 10) |
| --user-agent | Пользовательский User-Agent |
| --headers | Дополнительные заголовки в формате Key: Value (можно повторять) |
| --cookie | Строка cookie для отправки |
| --follow-redirects | Следовать перенаправлениям HTTP |
| --verify-ssl | Проверять SSL-сертификаты (по умолчанию: true) |
| --threads | Количество параллельных потоков (по умолчанию: 10) |
| --delay | Задержка между запросами в секундах |
| --output | Путь к выходному файлу |
| --format | Формат вывода: json, csv, table (по умолчанию: table) |
| --verbose | Включить подробное логирование |
| --quiet | Подавить весь вывод, кроме результатов |
| --config | Путь к файлу конфигурации |
# Базовое сканирование
python3 scanner.py -u https://example.com
# Сканирование со списком URL
python3 scanner.py -f urls.txt --threads 20
# Использование прокси и пользовательских заголовков
python3 scanner.py -u https://example.com --proxy http://127.0.0.1:8080 --headers "Authorization: Bearer token"
# Вывод в формате JSON
python3 scanner.py -u https://example.com --format json --output results.json
# Подробный режим с задержкой
python3 scanner.py -u https://example.com --verbose --delay 1
Инструмент поддерживает файл конфигурации в формате YAML. По умолчанию он ищет файл config.yaml в текущем каталоге.
# Пример config.yaml
targets:
- https://example.com
- https://test.example.com
options:
threads: 10
timeout: 10
follow_redirects: true
verify_ssl: true
user_agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
headers:
Authorization: "Bearer your-token-here"
X-Custom-Header: "custom-value"
output:
format: json
path: results.json
Инструмент поддерживает несколько форматов вывода:
+---------------------+--------+---------------------+
| URL | Статус | Детали |
+---------------------+--------+---------------------+
| https://example.com | 200 | OK |
| https://test.com | 404 | Not Found |
+---------------------+--------+---------------------+
{
"results": [
{
"url": "https://example.com",
"status": 200,
"details": "OK",
"timestamp": "2024-01-15T10:30:00Z"
}
],
"summary": {
"total": 1,
"success": 1,
"failed": 0
}
}
url,status,details,timestamp
https://example.com,200,OK,2024-01-15T10:30:00Z
Вы можете расширить функциональность инструмента, создав пользовательские модули. Каждый модуль должен реализовывать следующий интерфейс:
class CustomModule:
def __init__(self, options):
self.options = options
def run(self, target):
# Ваша логика здесь
return {
"url": target,
"status": "success",
"data": {}
}
Инструмент можно легко интегрировать в конвейеры CI/CD:
# Пример GitHub Actions
name: Security Scan
on: [push]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run scanner
run: |
python3 scanner.py -u https://example.com --format json --output results.json
- name: Upload results
uses: actions/upload-artifact@v2
with:
name: scan-results
path: results.json
Проблема: ModuleNotFoundError: No module named 'requests'
Решение: Установите отсутствующие зависимости:
pip install -r requirements.txt
Проблема: Тайм-аут соединения
Решение: Увеличьте значение тайм-аута или проверьте сетевое подключение:
python3 scanner.py -u https://example.com --timeout 30
Проблема: Ошибки SSL-сертификата
Решение: Отключите проверку SSL (только для тестирования):
python3 scanner.py -u https://example.com --verify-ssl false
Для получения подробной информации об отладке используйте флаг --verbose:
python3 scanner.py -u https://example.com --verbose
Это выведет подробные журналы, включая HTTP-запросы, ответы и внутренние операции.
Мы приветствуем вклад в развитие проекта! Пожалуйста, следуйте этим рекомендациям:
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)Этот проект лицензирован под лицензией MIT — подробности см. в файле LICENSE.
Этот инструмент предназначен только для образовательных целей и легального тестирования на проникновение. Пользователи несут ответственность за соблюдение всех применимых законов и получение надлежащего разрешения перед использованием этого инструмента в любой системе. Авторы не несут ответственности за любой ущерб, причинённый в результате использования или неправильного использования этого программного обеспечения.```nginx location /stop-bots/ { proxy_pass http://127.0.0.1:8787; # NO trailing slash proxy_set_header Host $host; }
**Завершающая косая черта в `proxy_pass` имеет значение, и именно её отсутствие — весь трюк.**
Без неё NGINX передаёт полный путь целиком, и `stop-bots` видит
`/stop-bots/whatever`, что он теперь и обслуживает, и генерирует. *С* завершающей
косой чертой NGINX отсекает префикс — и тогда браузер разрешает ссылки на странице
относительно корня домена, выходит за пределы блока `location`, и всё отдаёт 404. Никакая
аккуратность на стороне сервера это не исправит, поэтому префикс должен выжить при
проксировании.
Извне это ничем не принуждается, но сбой получается громким, а не тонким: при
настроенном префиксе запрос без префикса — это обычный 404, а не страница, которая
работает наполовину.
## Чего он не будет делать
Две вещи отсутствуют намеренно, и экран Help сообщает об этом вместе с причинами:
- **Он не разблокирует то, что заблокировал загруженный список** — следующее обновление этого
списка молча отменило бы это.
- **Он не меняет свой собственный пароль.** Используйте `stop-bots web --set-password` на хосте.
Он также отказывается блокировать адрес, с которого вы подключены, — это отняло бы
консоль, которой вы бы воспользовались, чтобы это отменить.
**Раньше их было три.** Применение скрипта брандмауэра было третьим, на том основании, что
его запуск — единственная операция, способная отключить хост от сети. Теперь это
доступно — «Apply everything» на Dashboard (`a` в TUI), или галочка «run it after
writing» на панели брандмауэра — потому что защита, делающая его безопасным при запуске из cron, делает его безопасным и при
нажатии кнопки: правила проверяются против клиентов, в данный момент подключённых по SSH, в
том порядке, в котором их будет вычислять сам скрипт, и правило, которое заблокировало бы одного из них, — это
отказ, а не предупреждение. Запустите консоль с `--no-apply`, чтобы вернуть прежнее
поведение только для записи.
Попытки входа ограничиваются по частоте. Не потому, что пароль можно угадать — он генерируется,
144 бита — а потому, что его проверка запускает Argon2id, и позволить неаутентифицированному вызывающему
управлять этим так быстро, как он только может отправлять запросы, — это отказ в обслуживании против хоста, который этот инструмент
должен защищать. Десять неверных попыток бесплатны; сверх этого клиент отступает
экспоненциально, а глобальный лимит ограничивает нагрузку на CPU независимо от того, со скольких адресов
приходят попытки.
## Запуск NGINX в контейнере
Если NGINX работает в Docker и его конфигурация находится на bind mount, `systemctl reload nginx` не перезагружает
ничего. Направьте обе команды на контейнер вместо этого — это относится и к CLI, и к
TUI:```
stop-bots set-nginx-commands \
--test "docker exec web nginx -t" \
--reload "docker exec web nginx -s reload"
Команда разбивается на слова и выполняется напрямую. Она никогда не проходит через оболочку, поэтому ;,
| и $VAR являются обычными символами, а не синтаксисом.
Как устроен код, как он тестируется и по каким правилам написан, описано в CONTRIBUTING.md. Процесс выпуска описан в RELEASING.md.
Вы можете связаться со мной по адресу [email protected].
Copyright (C) 2026 Marko Ivankovic
Эта программа является свободным программным обеспечением: вы можете распространять её и/или изменять на условиях GNU Affero General Public License, опубликованной Free Software Foundation, либо версии 3 Лицензии, либо (по вашему выбору) любой более поздней версии.
Полный текст Лицензии см. в файле LICENSE.
Альтернативное лицензирование НЕ доступно.
| Правило | Отклоняет, помимо ботов |
|---|
| HTTP/1.0 и HTTP/1.1 | краулеры и API-клиенты, не говорящие на HTTP/2 |
Нет заголовка Accept | некоторые API-клиенты не отправляют его |
Нет Accept-Language | инструменты приватности его удаляют |
Пустой/отсутствующий User-Agent | скрипты и проверки состояния часто его опускают |
Host — голый IP | ломает доступ к сайту по IP |
| TLS 1.0 / 1.1 | только очень старые клиенты |