Из названий было не видно, какая панель лежит в основе. Теперь версия стоит в H1 всех документов и галерей, а в начале README (обоих) добавлена строка об оригинале: HostinPL 5.6, авторы Samir Shelenko и Alexander Zemlyanoy, писалась под Debian 9 / PHP 7.0. Нумерация версии оставлена от оригинала. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
13 KiB
Русский · English
HostinPL 5.6 · What changed compared to the original
Original: HostinPL 5.6 as a "nulled" build (the Xopowblu-4EJlOBEK/HostinPL-5.6 fork),
written for Debian 9 and PHP 7.0.
Below is everything we changed, with file names and line numbers. Panel code changes live in panel/.
Maintained and developed with REDL.IO — AI-powered hosting.
1. The panel now runs on modern PHP
The original targets PHP 7.0. On PHP 8 parts of it died outright. It now runs on PHP 8.4: walking all 21 sections (home, servers, news, status, tickets, web hosting and the whole admin area) returns 200 on every one and zero errors in the log.
| File | Before | After |
|---|---|---|
engine/engine_ftp/elFinder.class.php:4497 |
utf8_encode() — removed in PHP 8.2, the file manager died with a fatal error |
Wrapped in function_exists(), falls back to mb_convert_encoding($str,'UTF-8','ISO-8859-1') |
application/views/admin/checksys/index.php:22 |
apache_get_modules() — only exists under Apache with mod_php, so the "System check" page returned 500 under nginx |
Wrapped in function_exists(), otherwise checked via REQUEST_URI |
application/views/admin/index.php:215 |
$item['invoice_ammount'] used after the foreach, producing an Undefined variable when there were no invoices |
isset() check |
application/models/users.php:199-206 |
$city[1], $country[1], $countryCode[1] used without checking that the regex matched |
isset() checks |
application/controllers/common/loginheader.php:35 |
$_GET['ref'] without isset |
isset() plus a cast to (int) |
What turned out to be a false alarm
The bundled phpseclib 1.x uses create_function(), which PHP 8 removed. It looked like a blocker,
but checking showed that phpseclib is not included anywhere in the panel — it is dead code.
Communication with game nodes goes through the native php-ssh2 extension
(engine/libs/ssh2.php → ssh2_exec). Nothing needed fixing.
get_magic_quotes_gpc() in elFinderConnector.class.php:320 is equally harmless: it sits behind
version_compare(PHP_VERSION,'5.4','<') && ..., so on PHP 8 it is never reached.
2. The captcha is off and managed from the admin area
The captcha used to be hard-wired: without valid Google keys it was impossible to log in,
and the form displayed "ERROR for site owner: Invalid site key". There is now a captcha_enable
flag in application/config.php, defaulting to 0 (off).
Backend. All four validators received the line
if($this->config->captcha_enable != '1') return $result; before the captcha check:
application/controllers/account/login.php— loginapplication/controllers/common/loginheader.php— registration and the contact form (2 places)application/controllers/tickets/create.php— ticket creation
Markup. The five captcha widgets (4 in views/common/loginheader.php, 1 in
views/tickets/create.php) are wrapped in <?php if(@$captcha_enable == '1'): ?>. Google's api.js
is only loaded when the captcha is on. Every grecaptcha.reset(...) call became
window.grecaptcha && grecaptcha.reset(...) — otherwise, with the captcha off, JavaScript threw
inside the error handlers and the forms stopped responding.
Admin area. views/admin/settings.php, the "Other settings" tab, gained a
"Bot protection (reCAPTCHA v2)" block: an Off/On selector plus Site key and Secret key fields.
Verified both ways: with the captcha off, login and registration succeed and the user is really created in the database; with it on, a fresh session gets "Confirm that you are not a robot!" and the widget returns to the page.
Careful when adding your own settings. The settings writer matches configuration lines by substring (
strpos). That is why the flag is calledcaptcha_enableand notcaptcha: the stringcaptchaoccurs insiderecaptchaandsecret_recaptcha, and saving would have overwritten the wrong parameter. New keys must not be substrings of existing ones.
3. Vulnerabilities fixed
In brief (full detail with code in SECURITY.en.md):
- Two pre-authentication SQL injections in
application/models/users.php(createAuthLog()): the login journal received the password from the form and the IP from theCF-Connecting-IPheader — which was not validated at all — without escaping. Every value now goes through$this->db->escape()or a cast to(int), and the header is validated withfilter_var(..., FILTER_VALIDATE_IP). - Plaintext passwords:
login.phpwrote the real password into theauthlogtable on every login attempt, including failed ones. Removed. - XSS in the hidden
reffield of the registration form —htmlspecialchars()added. - The request to the
ip-api.comgeolocation service now only runs for a valid IP and usesurlencode().
4. The installer was rewritten from scratch
The original install was dangerous on any current system:
echo "deb ... stretch main" > /etc/apt/sources.list— wiped the repository list and replaced it with Debian 9, after which apt was broken- installed
php7.0and hard-coded edits to/etc/php/7.0/apache2/php.ini - switched MariaDB to
bind-address = 0.0.0.0without a word of warning - only ran when
/etc/issue.netsaidDebian9, and otherwise refused to start - generated passwords and tokens but saved them nowhere — you could only copy them off the screen
- carried on after any failure: every command ended in
> /dev/null 2>&1
The new scripts:
install-panel.sh — nginx, PHP 8.x (version detected automatically), MariaDB, a database with a
random password, configuration, scheduler, autostart watchdog, administrator creation, and a final
check that the login page really is served with its form. Idempotent: running it again does not wipe
a database that is already loaded. Credentials are written to /root/.redl-panel-credentials
(chmod 600). With set -euo pipefail the script stops on error instead of pretending all is well.
install-node.sh — Docker from the official repository, image build, the
/home/cp/gameservers/files layout, the gameservers group, MariaDB for game server databases,
SteamCMD, ProFTPD, and sshd configuration that rolls back if sshd -t fails. Before doing anything
it checks whether Docker can work on this machine at all (unshare -Ur) and says so plainly if it
cannot. At the end it prints ready-to-use location details and firewall commands.
Neither script touches /etc/apt/sources.list.
5. The game server image was rebuilt
The original docker/Dockerfile.original-stretch is based on debian:stretch, and the Debian 9
repositories were shut down in 2023 — that image no longer builds, apt-get update inside it fails.
The new docker/Dockerfile is based on Debian 12 (bookworm) but must still be tagged
debian:stretch: that name is hard-coded in the panel
(application/models/servers.php:642, docker create ... debian:stretch) and cannot be changed
without editing the panel.
What is inside: 32-bit libraries (SA-MP, CRMP, MTA and older CS builds are i386), screen for
consoles, Java for Minecraft, Node.js 20 for RAGE:MP, and gdb for crash analysis.
Node.js comes from NodeSource over HTTPS with repository key verification, unlike before.
6. Working without systemd
The panel assumes systemd is present. Container VPSes do not have it, so watchdogs were added on
cron: /usr/local/bin/hostinpl-guard (panel) and /usr/local/bin/gamenode-guard (node).
Once a minute (every 2 minutes on a node) they check MariaDB, PHP-FPM, nginx, Docker and cron and
restart whatever died, plus an @reboot job to bring everything up after a restart.
Panel liveness is determined by an HTTP request to the login page rather than by searching for a process name — a pattern search would have matched the watchdog's own command line.
7. Scheduler
The original pointed its 9 jobs at the panel's public domain. They now go to 127.0.0.1
(independent of DNS and external reachability) and carry -m timeouts so that a hung request does
not pile up processes.
Token pitfall. You cannot extract the scheduler token from the config with a plain
grep "'token'"— the line'yk_password1' => 'token'matches the same pattern, two values end up in the URL separated by a newline, and the jobs then fail silently. The installer writes the token directly when it generates the configuration.
8. Interface languages: Russian and English
The original was Russian-only, with every string hard-coded in the templates. The build now has a translation layer:
engine/main/lang.php— theLangclass and the globalt()helperapplication/lang/ru.php,application/lang/en.php— dictionaries- Language selection order:
?lang=parameter →langcookie → the browser'sAccept-Languageheader → panel default - A switcher (RU / EN) in the header of the client area and on the login page
- Anything not yet translated falls back to the original Russian text, so nothing can disappear from the interface
Translation is applied to the finished response via ob_start(), so none of the 200 template files
had to be rewritten and the coverage extends to the admin area and the e-mails at once. Replacement
only happens on word boundaries — without that a short key corrupts longer words ("Мод" turned
"Модуль" into "Modуль").
Translation coverage
The dictionary holds 1304 phrases — everything the panel shows a human: the client area, ordering a server, server management (the console with every RCON command described, FTP, MySQL databases, the firewall, the task scheduler, mod auto-install), tickets, the whole FAQ, the entire admin area, the e-mails and the scheduler's status messages.
Coverage is not eyeballed: a script replays the replacement logic against the sources and lists what would stay Russian. By that measure four items are uncovered, and all four should be:
- "Русский" — the label of the language switcher itself;
rus => Pyccĸийandukr => Українськаin the FAQ — these are game server language codes, not UI;- the name validation regular expression
/^([А-ЯЁ])([а-яё]{1,15})$/u— code, not text.
Two traps that produced mixed-language output were fixed separately:
- the preposition "в" was its own dictionary entry and got substituted inside sentences that were not translated yet, producing "at поле имя пользователя". Function words like that are only translated together with the phrase they belong to;
- the date format
d.m.Y в H:icontains the Russian "в" between the date and the time. It now goes throught()(31date()calls across 20 files), so English mode renders30.07.2026 14:22.
Verified by a live crawl of 45 pages in English mode, including the creation forms and the hidden settings tabs. Details: see GUIDE.en.md, the "Interface language" section.
9. Branding and dates
- The year in footers:
2020©→2026©(views/common/footer.php,views/common/loginheader.php) - Name and description are REDL.IO:
application/config.php(description,keywords,mail_sender,mail_from), footers, the "System check" page - Links to the previous build owner's sites (
hostinpl.ru,osmp.ga) replaced withredl.io - "VK community" links pointing at somebody else's community replaced with
redl.io(footer.php,views/main/index.php,views/offline/index.php) - All 15 e-mail templates: a "REDL.IO / AI-powered hosting" header and the signature "Sincerely, the REDL.IO team"
The authors' copyright headers were not removed — they remain in every file that had them.
10. phpMyAdmin
The panel links to /phpmyadmin from the admin area. The original installer set phpMyAdmin up
through Apache, which did not work under nginx. It is now installed from the distribution repository
and served by nginx itself.
Reverse proxy pitfall. For the address
/phpmyadmin(without a trailing slash) nginx replies with a redirect and by default puts its own port into it, for examplehttp://panel.example.com:8095/phpmyadmin/. If the panel sits behind a reverse proxy that address is unreachable from outside and the admin link does not open. The cure isabsolute_redirect off; port_in_redirect off;, already present in the configuration the installer generates.
What we did NOT do
- We did not rewrite password hashing. The panel stores passwords as unsalted MD5
(
md5($password), avarchar(32)column). Moving topassword_hash()affects login, registration, recovery and password changes, and requires migrating existing users. - We did not move game server creation away from Docker.
- We did not delete
panel/application/public/js/proxy/proxy.php— the file is defanged but kept as evidence (see SECURITY.en.md). - We did not test the payment gateways with real payments, nor VK login.
- We did not verify the game servers themselves: that needs a node with Docker.
Maintained and developed with REDL.IO — AI-powered hosting.