Сборка REDL: панель на PHP 8, капча-тумблер, закрытые уязвимости, установщики в один клик

- панель работает на PHP 8.4 (оригинал под PHP 7.0): elFinder utf8_encode, apache_get_modules, warnings
- капча выключена и управляется из админки (флаг captcha_enable + блок настроек)
- закрыты две SQL-инъекции без авторизации (createAuthLog: пароль из POST и CF-Connecting-IP)
- убрано хранение паролей в открытом виде в authlog, XSS в поле ref
- установщики install-panel.sh и install-node.sh (оригинальный затирал sources.list репозиториями Debian 9)
- современный Dockerfile (оригинальный на debian:stretch больше не собирается)
- планировщик и автозапуск работают без systemd
- брендинг REDL.IO, год 2026, ссылки на redl.io
- документация на русском: ИЗМЕНЕНИЯ, БЕЗОПАСНОСТЬ, ИНСТРУКЦИЯ
This commit is contained in:
2026-07-30 02:00:01 +00:00
commit 6ee2744e3a
492 changed files with 354172 additions and 0 deletions
+200
View File
@@ -0,0 +1,200 @@
# Что изменено по сравнению с оригиналом
Оригинал: **HostinPL 5.6** в виде «nulled»-сборки (форк `Xopowblu-4EJlOBEK/HostinPL-5.6`),
написанной под Debian 9 и PHP 7.0.
Ниже — всё, что мы поменяли, с указанием файлов и строк. Правки в коде панели лежат в `panel/`.
**Поддерживается и развивается с помощью [REDL.IO](https://redl.io) — Хостинг с искусственным интеллектом.**
---
## 1. Панель запущена на современном PHP
Оригинал написан под PHP 7.0. На PHP 8 часть кода падала насмерть. Сейчас панель работает
на **PHP 8.4**: обход всех 21 раздела (главная, сервера, новости, статус, тикеты, веб-хостинг
и вся админка) даёт **200 на каждом и ноль ошибок в логе**.
| Файл | Было | Стало |
|------|------|-------|
| `engine/engine_ftp/elFinder.class.php:4497` | `utf8_encode()` — функция удалена в PHP 8.2, файловый менеджер падал с фатальной ошибкой | Обёрнута в `function_exists()`, фолбэк `mb_convert_encoding($str,'UTF-8','ISO-8859-1')` |
| `application/views/admin/checksys/index.php:22` | `apache_get_modules()` — существует только в Apache с mod_php, под nginx раздел «Проверка системы» отдавал 500 | Обёрнута в `function_exists()`, иначе проверка по `REQUEST_URI` |
| `application/views/admin/index.php:215` | `$item['invoice_ammount']` использовался после `foreach`, при пустом списке счетов — Undefined variable | Проверка `isset()` |
| `application/models/users.php:199-206` | `$city[1]`, `$country[1]`, `$countryCode[1]` без проверки совпадения регулярки | Проверки `isset()` |
| `application/controllers/common/loginheader.php:35` | `$_GET['ref']` без `isset` | `isset()` + приведение к `(int)` |
### Что оказалось ложной тревогой
Библиотека **phpseclib 1.x** в составе панели использует `create_function()`, удалённую в PHP 8.
Выглядело как блокирующая проблема, но проверка показала: **phpseclib не подключается ни одним
`include` во всей панели** — это мёртвый код. Связь с игровыми нодами идёт через нативное
расширение `php-ssh2` (`engine/libs/ssh2.php``ssh2_exec`). Ничего чинить не потребовалось.
Так же безопасен `get_magic_quotes_gpc()` в `elFinderConnector.class.php:320` — он закрыт
условием `version_compare(PHP_VERSION,'5.4','<') && ...`, и на PHP 8 до вызова дело не доходит.
---
## 2. Капча выключена и управляется из админки
Раньше капча была вшита жёстко: без валидных ключей Google **войти в панель было невозможно**,
на форме висело «ERROR for site owner: Invalid site key». Теперь появился флаг
`captcha_enable` в `application/config.php` (по умолчанию `0` — выключена).
**Бэкенд.** Во все четыре валидатора добавлена строка
`if($this->config->captcha_enable != '1') return $result;` перед проверкой капчи:
* `application/controllers/account/login.php` — вход
* `application/controllers/common/loginheader.php` — регистрация и форма обратной связи (2 места)
* `application/controllers/tickets/create.php` — создание тикета
**Вёрстка.** Пять виджетов капчи (4 в `views/common/loginheader.php`, 1 в `views/tickets/create.php`)
обёрнуты в `<?php if(@$captcha_enable == '1'): ?>`. Скрипт Google `api.js` подключается только при
включённой капче. Все вызовы `grecaptcha.reset(...)` заменены на
`window.grecaptcha && grecaptcha.reset(...)` — иначе при выключенной капче JavaScript падал
в обработчиках ошибок и формы переставали отвечать.
**Админка.** В `views/admin/settings.php`, вкладка «Прочие настройки», добавлен блок
«Защита от ботов (reCAPTCHA v2)»: переключатель Выключена/Включена и поля Site key и Secret key.
Проверено в обе стороны: при выключенной капче вход и регистрация проходят и пользователь реально
создаётся в базе; при включённой — свежая сессия получает «Подтвердите, что вы не робот!» и виджет
возвращается на страницу.
> **Осторожно при добавлении своих настроек.** Механизм сохранения настроек ищет строки конфига
> **по подстроке** (`strpos`). Поэтому флаг назван `captcha_enable`, а не `captcha`: строка
> `captcha` содержится в `recaptcha` и `secret_recaptcha`, и сохранение перезаписало бы не тот
> параметр. Новые ключи не должны быть подстрокой существующих.
---
## 3. Закрыты уязвимости
Кратко (подробно с кодом — в [БЕЗОПАСНОСТЬ.md](БЕЗОПАСНОСТЬ.md)):
* **Две SQL-инъекции, доступные без авторизации** в `application/models/users.php`
(`createAuthLog()`): в журнал входов без экранирования попадали пароль из формы и IP из
заголовка `CF-Connecting-IP`, который вообще не проверялся. Все значения теперь проходят через
`$this->db->escape()` / `(int)`, заголовок проверяется `filter_var(..., FILTER_VALIDATE_IP)`.
* **Пароли в открытом виде**: `login.php` писал реальный пароль в таблицу `authlog` при каждой
попытке входа, включая неудачные. Убрано.
* **XSS** в скрытом поле `ref` формы регистрации — добавлен `htmlspecialchars()`.
* Запрос к геосервису `ip-api.com` выполняется только для валидного IP и с `urlencode()`.
---
## 4. Установщик переписан с нуля
Оригинальный `install` был опасен на любой современной системе:
* `echo "deb ... stretch main" > /etc/apt/sources.list`**затирал список репозиториев**
и заменял его на Debian 9, после чего apt ломался
* ставил `php7.0` и жёстко правил `/etc/php/7.0/apache2/php.ini`
* переводил MariaDB на `bind-address = 0.0.0.0` без единого предупреждения
* работал только при `/etc/issue.net` == `Debian9`, иначе просто отказывался запускаться
* пароли и токены генерировал, но нигде не сохранял — их можно было только записать с экрана
* при любой ошибке продолжал работу: все команды заканчивались на `> /dev/null 2>&1`
Новые скрипты:
**`install-panel.sh`** — nginx, PHP 8.x (версия определяется автоматически), MariaDB, база со
случайным паролем, конфиг, планировщик, сторож автозапуска, создание администратора и финальная
проверка, что страница входа действительно отдаётся с формой. Идемпотентен: повторный запуск
не затирает уже загруженную базу. Креды пишутся в `/root/.redl-panel-credentials` (chmod 600).
`set -euo pipefail` — при ошибке скрипт останавливается, а не делает вид, что всё хорошо.
**`install-node.sh`** — Docker из официального репозитория, сборка образа, каталоги
`/home/cp/gameservers/files`, группа `gameservers`, MariaDB для баз игровых серверов, SteamCMD,
ProFTPD, настройка sshd с откатом конфига, если `sshd -t` не проходит. **Перед началом проверяет,
может ли Docker вообще работать на этой машине** (`unshare -Ur`), и честно предупреждает, если нет.
В конце печатает готовые данные для подключения локации и команды файрвола.
Ни один из скриптов не трогает `/etc/apt/sources.list`.
---
## 5. Образ игровых серверов пересобран
Оригинальный `docker/Dockerfile.original-stretch` собран на `debian:stretch`, а репозитории
Debian 9 отключены с 2023 года — **образ больше не собирается**, `apt-get update` внутри падает.
Новый `docker/Dockerfile` — на Debian 12 (bookworm), но **обязательно с тегом `debian:stretch`**:
это имя жёстко прописано в коде панели (`application/models/servers.php:642`,
`docker create ... debian:stretch`), менять его без правки панели нельзя.
Что внутри: 32-битные библиотеки (SA-MP, CRMP, MTA, старые CS собраны под i386), `screen` для
консолей, Java для Minecraft, Node.js 20 для RAGE:MP, `gdb` для разбора крашей.
Node.js берётся из NodeSource **по HTTPS с проверкой ключа**, а не как раньше.
---
## 6. Работа без systemd
Панель штатно рассчитана на systemd. В контейнерных VPS его нет, поэтому добавлены сторожа
на cron: `/usr/local/bin/hostinpl-guard` (панель) и `/usr/local/bin/gamenode-guard` (нода).
Раз в минуту (нода — раз в 2 минуты) они проверяют MariaDB, PHP-FPM, nginx, Docker и cron и
поднимают то, что упало; плюс задание `@reboot` для подъёма после перезагрузки.
Живость панели определяется **HTTP-запросом** к странице входа, а не поиском процесса по имени —
поиск по шаблону ловил бы собственную командную строку сторожа.
---
## 7. Планировщик
Оригинал прописывал 9 заданий на публичный домен панели. Теперь они ходят на `127.0.0.1`
(не зависит от DNS и внешней доступности) и получают таймауты `-m`, чтобы зависший запрос
не копился в процессах.
> **Грабля с токеном.** Токен планировщика нельзя выцеплять из конфига простым `grep "'token'"` —
> под это же условие попадает строка `'yk_password1' => 'token'`, и в URL уезжают два значения
> через перевод строки, после чего задания молча не работают. В установщике токен пишется
> напрямую при генерации конфига.
---
## 8. Брендинг и даты
* Год в подвалах: `2020©``2026©` (`views/common/footer.php`, `views/common/loginheader.php`)
* Название и описание — REDL.IO: `application/config.php` (`description`, `keywords`,
`mail_sender`, `mail_from`), подвалы, раздел «Проверка системы»
* Ссылки на сторонние сайты прежнего владельца сборки (`hostinpl.ru`, `osmp.ga`) заменены на `redl.io`
* Ссылки «Сообщество VK» на чужое сообщество заменены на `redl.io` (`footer.php`,
`views/main/index.php`, `views/offline/index.php`)
* Все 15 шаблонов писем: шапка «REDL.IO / Хостинг с искусственным интеллектом», подпись
«С уважением, Администрация REDL.IO»
**Авторские копирайт-заголовки в коде не удалялись** — они остались во всех файлах,
где были изначально.
---
## 9. phpMyAdmin
Панель ссылается на `/phpmyadmin` из админки. Оригинальный установщик ставил phpMyAdmin через
Apache, что на nginx не работало. Теперь он ставится из репозитория дистрибутива и отдаётся
самим nginx.
> **Грабля за обратным прокси.** nginx на адрес `/phpmyadmin` (без слеша) отвечает редиректом
> и по умолчанию подставляет в него **собственный порт**, например
> `http://panel.example.com:8095/phpmyadmin/`. Если панель стоит за обратным прокси, такой адрес
> снаружи недоступен и ссылка из админки не открывается. Лечится
> `absolute_redirect off; port_in_redirect off;` — это уже прописано в конфиге, который создаёт
> установщик.
---
## Чего мы НЕ делали
* Не переписывали хеширование паролей. Панель хранит пароли как **MD5 без соли**
(`md5($password)`, колонка `varchar(32)`). Переход на `password_hash()` затрагивает вход,
регистрацию, восстановление, смену пароля и требует миграции существующих пользователей.
* Не переводили создание игровых серверов с Docker на что-то другое.
* Не удаляли `panel/application/public/js/proxy/proxy.php` — файл обезврежен, но оставлен как
свидетельство (см. [БЕЗОПАСНОСТЬ.md](БЕЗОПАСНОСТЬ.md)).
* Не тестировали платёжные шлюзы живыми платежами и VK-авторизацию.
* Не проверяли работу самих игровых серверов: для этого нужна нода с Docker.
---
**Поддерживается и развивается с помощью [REDL.IO](https://redl.io) — Хостинг с искусственным интеллектом.**