/
streamlive
/
StreamLiveV11
Обзор
Документация
Войти
/
streamlive
/
StreamLiveV11
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
master
includes/antibot.php
144 строки
9 KB
TwixoffStudio
Create: ai_client.php, antibot.php, auth.php, bbcode.php, bbcode_toolbar.php, db.php, embed_helper.php, footer.php, functions.php, geo.php, header.php, migrations.php, mini_player.php, oauth.php, rss.php, stats.php, totp.php
15 июл 2026, 01:16
Верифицирован
15 июл 2026, 01:16
a3a3d10
Код
Авторство
О чём код?
<?php // «Ты не робот?» — простая защита от дудоса/ботов без внешних сервисов (Google/Yandex // captcha недоступны без ключей и не гарантированно работают на бесплатном хостинге). // Работает полностью на своей SVG-капче — не требует GD и вообще никаких расширений PHP. // // Логика: // 1. Считаем «настоящие» просмотры страниц с одного IP (не AJAX/поллинг — иначе обычный // зритель, у которого чат/расписание обновляются раз в 15 сек, огребёт капчу почти сразу). // 2. Если за короткое окно накопилось больше ANTIBOT_LIMIT запросов — блокируем IP на // ANTIBOT_BLOCK_HOURS часов и показываем капчу вместо страницы. // 3. Правильный ответ — сразу снимает блокировку и пускает дальше. // 4. Неправильный — можно пробовать ещё (новая капча), но блокировка сама снимается // по истечении 24 часов, даже если капчу так и не разгадали. const ANTIBOT_LIMIT = 5; // сколько просмотров страниц допускается const ANTIBOT_WINDOW_SEC = 10; // за какой промежуток времени (секунды) const ANTIBOT_BLOCK_HOURS = 24; // на сколько блокируем при превышении // Пути, которые никогда не считаются и не блокируются — иначе сломается сам сайт // (AJAX/поллинг вызывается каждые несколько секунд у любого активного зрителя, // а сама капча должна быть доступна тому, кого уже заблокировали). function antibot_exempt_paths(): array { return [ '/antibot_challenge.php', '/antibot_verify.php', '/antibot_image.svg.php', '/now_playing.php', '/chat_poll.php', '/chat_send.php', '/chat_delete.php', '/chat_ban.php', '/message_poll.php', '/message_send.php', '/user_search.php', '/check_stream_live.php', '/broadcast_post_poll.php', '/broadcast_post_send.php', '/rss_forum.php', '/rss_channels.php', '/channel_like.php', '/channel_favorite.php', '/short_like.php', '/ai_send.php', '/ai_project_save.php', '/comment_add.php', '/comment_delete.php', '/sitemap.xml.php', '/robots.txt.php', ]; } function antibot_client_ip(): string { // На большинстве бесплатного шаред-хостинга сайт не за собственным прокси/CDN, // так что REMOTE_ADDR обычно надёжен. X-Forwarded-For берём только как запасной // вариант и на всякий случай валидируем формат, чтобы его нельзя было тривиально подделать // и накрутить блокировку постороннему IP. $candidate = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'; if (empty($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $first = trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0]); if (filter_var($first, FILTER_VALIDATE_IP)) $candidate = $first; } return $candidate; } function antibot_ensure_table(): void { try { db()->exec( 'CREATE TABLE IF NOT EXISTS antibot_ip_log ( ip VARCHAR(45) PRIMARY KEY, request_count INT NOT NULL DEFAULT 0, window_start DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, blocked_until DATETIME DEFAULT NULL, captcha_code VARCHAR(10) DEFAULT NULL, captcha_expires DATETIME DEFAULT NULL, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4' ); } catch (\Throwable $e) { /* уже есть, или нет прав на CREATE — тогда просто пропустим защиту ниже */ } } // Генерирует новый код капчи для этого IP и возвращает его (для рендера SVG). function antibot_new_code(string $ip): string { $alphabet = 'АБВГДЕЖКМНПРСТУХ23456789'; // без похожих друг на друга символов // ВАЖНО: кириллица в UTF-8 — это 2 байта на букву. Обращение к строке через [] // берёт БАЙТ, а не символ, и портит кодировку (MySQL потом отказывается сохранять // такую строку). Через mb_substr() берём именно символ. $alphabetLen = mb_strlen($alphabet); $code = ''; for ($i = 0; $i < 5; $i++) $code .= mb_substr($alphabet, random_int(0, $alphabetLen - 1), 1); $expires = date('Y-m-d H:i:s', time() + 600); // 10 минут, считаем в PHP (см. antibot_guard про часовые пояса) db()->prepare('UPDATE antibot_ip_log SET captcha_code = ?, captcha_expires = ? WHERE ip = ?') ->execute([$code, $expires, $ip]); return $code; } // Главная проверка — вызывается в самом начале обработки запроса (из functions.php). // // ВАЖНО: все временные метки здесь считаются и пишутся из PHP (date('Y-m-d H:i:s')), // а не через MySQL NOW()/DATE_ADD. На бесплатном хостинге часовой пояс самого сервера // MySQL нам не подконтролен и обычно UTC, а PHP здесь настроен на Europe/Moscow — // если писать NOW() в базу, а потом сравнивать через PHP strtotime(), время разъедется // на 3 часа и лимит запросов вообще никогда не сработает. Считаем всё только в PHP. function antibot_guard(): void { $path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH); if (in_array($path, antibot_exempt_paths(), true)) return; // Панель администратора не блокируем антиботом вообще — админ не должен случайно // сам себя запереть на 24 часа, кликая по своим же страницам. if (strpos((string)$path, '/admin/') === 0) return; try { antibot_ensure_table(); $ip = antibot_client_ip(); // УДАЛЁН БЛОК ИСКЛЮЧЕНИЯ ДЛЯ АВТОРИЗОВАННЫХ ПОЛЬЗОВАТЕЛЕЙ // Теперь все пользователи (включая авторизованных) проходят через полную проверку. // Административные пути и исключённые скрипты по-прежнему игнорируются. $pdo = db(); $nowStr = date('Y-m-d H:i:s'); $stmt = $pdo->prepare('SELECT * FROM antibot_ip_log WHERE ip = ?'); $stmt->execute([$ip]); $row = $stmt->fetch(); if ($row && $row['blocked_until'] && strtotime($row['blocked_until']) > time()) { antibot_show_challenge($ip, false); return; } if (!$row) { $pdo->prepare('INSERT INTO antibot_ip_log (ip, request_count, window_start) VALUES (?, 1, ?)')->execute([$ip, $nowStr]); return; } $windowAge = time() - strtotime($row['window_start']); if ($windowAge > ANTIBOT_WINDOW_SEC) { $pdo->prepare('UPDATE antibot_ip_log SET request_count = 1, window_start = ? WHERE ip = ?')->execute([$nowStr, $ip]); return; } $newCount = (int)$row['request_count'] + 1; if ($newCount > ANTIBOT_LIMIT) { $blockedUntil = date('Y-m-d H:i:s', time() + ANTIBOT_BLOCK_HOURS * 3600); $pdo->prepare('UPDATE antibot_ip_log SET blocked_until = ? WHERE ip = ?')->execute([$blockedUntil, $ip]); antibot_show_challenge($ip, true); return; } $pdo->prepare('UPDATE antibot_ip_log SET request_count = ? WHERE ip = ?')->execute([$newCount, $ip]); } catch (\Throwable $e) { // Если таблицы/БД временно недоступны — не роняем весь сайт из-за антибота, // просто пропускаем проверку на этот запрос. return; } } function antibot_show_challenge(string $ip, bool $justBlocked): void { http_response_code(429); $returnTo = $_SERVER['REQUEST_URI'] ?? '/'; antibot_new_code($ip); require __DIR__ . '/../antibot_challenge_view.php'; exit; }