Проверка кода на безопасность

InstantCMS 2.X

вопрос к спецам

#1 10 ноября 2026 в 13:46

Всем мира!!
Товарищи специалисты по безопасности кода, написал код помогала нейронка, для выгрузки базы данных по крону в своем контроллере
Предскажите на живой проект безопасно это ставить? может что поправить ?
так же дамп на диск облачный лучше грузить или на почте оставлять? конечно лучше бы как то что бы автоматом себе на комп, но пока что только на почту отправляет!

  1. <?php
  2. class onRxCronBacapBd extends cmsAction {
  3.  
  4. public function run() {
  5.  
  6. $site_dir = dirname(__DIR__, 3);
  7. $config_path = $site_dir . '/config/config.php';
  8.  
  9. //// --- Чтение конфига ---
  10. if (!is_file($config_path)) {
  11. http_response_code(500);
  12. exit('Config not found: ' . $config_path);
  13. }
  14.  
  15. $returned = require $config_path;
  16.  
  17. $cfg = null;
  18. if (is_array($returned)) {
  19. $cfg = $returned;
  20. } elseif (isset($config) && is_array($config)) {
  21. $cfg = $config;
  22. } elseif (isset($settings) && is_array($settings)) {
  23. $cfg = $settings;
  24. }
  25.  
  26. if (!$cfg) {
  27. http_response_code(500);
  28. exit('Cannot find config array');
  29. }
  30.  
  31. $db_host = $cfg['db_host'] ?? null;
  32. $db_user = $cfg['db_user'] ?? null;
  33. $db_pass = $cfg['db_pass'] ?? null;
  34. $db_name = $cfg['db_base'] ?? null;
  35. $sitename = $cfg['sitename'] ?? 'Site';
  36. $mail_from = $cfg['mail_from'] ?? 'site@mail.ru';
  37.  
  38.  
  39. // Папка для временного хранения бэкапа (желательно ВНЕ веб-корня!)
  40. $root_dir = dirname(__DIR__, 5);
  41. $backup_dir = $root_dir . '/backup_work/';
  42.  
  43.  
  44. if (!is_dir($backup_dir)) {
  45. if (!@mkdir($backup_dir, 0700, true)) {
  46. exit('mkdir failed: ' . (error_get_last()['message'] ?? '?'));
  47. }
  48. }
  49.  
  50. $lock_file = $backup_dir . '.backup.lock';
  51. $fp = fopen($lock_file, 'c');
  52.  
  53. if (!flock($fp, LOCK_EX | LOCK_NB)) {
  54. return true; // уже выполняется, выходим
  55. }
  56.  
  57. // Проверка, что папка для бэкапов существует и доступна на запись
  58. if (!is_dir($backup_dir) || !is_writable($backup_dir)) {
  59. http_response_code(500);
  60. exit('Backup directory is not writable');
  61. }
  62.  
  63. // 3. Готовим имена файлов ---
  64. $backup_file = $backup_dir . $db_name . '_backup_' . date('Y-m-d_H-i-s') . '.sql';
  65. $config_file = tempnam(sys_get_temp_dir(), 'mysql_config_');
  66. chmod($config_file, 0600);
  67.  
  68. $config_content = "[client]\n";
  69. $config_content .= "host=" . $db_host . "\n";
  70. $config_content .= "user=" . $db_user . "\n";
  71. $config_content .= "password=" . $db_pass . "\n";
  72. file_put_contents($config_file, $config_content);
  73.  
  74. $safe_config = escapeshellarg($config_file);
  75. $safe_name = escapeshellarg($db_name);
  76. $safe_file = escapeshellarg($backup_file);
  77.  
  78. // 4. Запуск mysqldump ---
  79. $err_file = $backup_file . '.err';
  80. $command = "mysqldump --defaults-extra-file={$safe_config} {$safe_name} > {$safe_file} 2>{$err_file}";
  81. exec($command, $output, $return_var);
  82.  
  83. if ($return_var !== 0) {
  84. $err = @file_get_contents($err_file);
  85. @unlink($backup_file);
  86. @unlink($err_file);
  87. error_log('Backup failed: ' . substr((string)$err, 0, 500));
  88. flock($fp, LOCK_UN); fclose($fp);
  89. return true;
  90. }
  91. @unlink($err_file);
  92.  
  93. // 5. Удаляем временный конфиг с паролем ---
  94. if (file_exists($config_file)) {
  95. unlink($config_file);
  96. }
  97.  
  98. // Email (если файл небольшой)
  99. if (filesize($backup_file) < 20 * 1024 * 1024) {
  100. $mailer = new cmsMailer();
  101. $mailer->setFrom($mail_from, 'BacapBD');
  102. $mailer->addTo('my_email@mail.ru', 'Получатель');
  103. $mailer->setSubject('BacapBD '.$sitename);
  104. $mailer->addAttachment($backup_file);
  105. $mailer->setBodyHTML('BacapBD for '.$sitename);
  106. $mailer->send();
  107. $mailer->clearTo()->clearAttachments();
  108. }
  109.  
  110. // Удаляем бэкап с сервера после отправки
  111. @unlink($backup_file);
  112.  
  113. flock($fp, LOCK_UN);
  114. fclose($fp);
  115.  
  116. return true;
  117.  
  118. }
  119. }
#2 10 ноября 2026 в 14:16

Прочитал тему. Автор просит проверить код резервного копирования БД. В таком виде ставить на рабочий сайт рискованно.

В опубликованном коде есть конкретные проблемы:

Дамп удаляется без подтверждения отправки: результат send() не проверяется. При размере от 20 МБ отправка вообще пропускается, а файл всё равно удаляется.
При ошибке mysqldump остаётся временный конфиг с паролем БД. Очистку нужно выполнять через finally.
Путь $err_file подставляется в shell-команду без escapeshellarg().
Значения логина и пароля записываются в конфиг MySQL без обработки специальных символов.
Не проверяются результаты fopen(), tempnam(), записи конфига; права на сам дамп явно не устанавливаются.
Для изменяющейся InnoDB-базы стоит предусмотреть --single-transaction: сейчас согласованность резервной копии не обеспечена.
Хранение вне веб-корня и блокировка flock() — полезные меры, но перечисленные ошибки они не устраняют.

Для хранения предпочтительнее зашифрованная копия на отдельном хранилище с историей версий, а почту использовать для уведомлений. Удалять локальный дамп следует только после подтверждённой загрузки; восстановление нужно отдельно проверить.

#3 10 ноября 2026 в 14:26

БЕЗОПАСНОЕ РЕЗЕРВНОЕ КОПИРОВАНИЕ БД INSTANTCMS
10.11.2026. Вариант исходного форумного обработчика rx / bacap_bd.

Это исходник хука, НЕ установочный пакет дополнения. Через установщик CMS
этот ZIP устанавливать не нужно. Ядро и таблицы CMS не изменяются.
Требуется существующий компонент rx. Для другого компонента изменить имя
класса onRxCronBacapBd и путь. Совместимость по синтаксису: PHP 7.4+,
64-bit Linux, InstantCMS 2.17.2+. API cmsConfig/cmsMailer сверены с 2.18.2.
Реальные сайт, СУБД и почтовый сервер автора не были доступны для проверки.

УСТАНОВКА
1. Сохранить исходный cron_bacap_bd.php и временно отключить эту задачу.
2. От имени пользователя, который запускает PHP cron, создать отдельную
   папку вне всех каталогов сайтов, alias и открываемых по HTTP хранилищ:
       mkdir -m 700 /absolute/private/icms-db-backups
   Родитель должен существовать; все предки — без записи группе/остальным.
   Пример пути условный. Не размещать в public_html, uploads, cache или /tmp.
   Проверка кода учитывает известные CMS web-root, но не знает alias nginx
   и виртуальные хосты. Исключить HTTP-доступ обязан администратор сервера.
3. В начале PHP-файла задать BACKUP_DIR и DUMP_BINARY. Папка должна иметь
   права ровно 0700 и принадлежать пользователю cron. Указать реальный полный
   путь к mysqldump / mariadb-dump, соответствующему серверу БД.
4. По умолчанию EMAIL_TO пуст: создаются только локальные SQL + SHA-256.
   Для почты указать свой адрес в EMAIL_TO. Требуется расширение PHP zip
   с AES-256; скрипт проверяет шифрование и расшифрованный SHA-256 перед отправкой.
   Задать ICMS_BACKUP_ZIP_PASSWORD в защищенном окружении задания cron.
   Пароль — случайный, не менее 24 символов; лучше 32 случайных байта в hex.
   Хранить отдельно в менеджере паролей. Не помещать в URL, командную строку,
   письмо с архивом, публичную инструкцию или репозиторий.
   Учесть: среда задания cron обычно отличается от среды терминала.
5. При нестандартном подключении задать ICMS_BACKUP_DB_PORT и/или
   ICMS_BACKUP_DB_SOCKET в окружении. db_host должен быть хостом, а не URL
   или строкой host:port. Пароль БД читается из загруженной конфигурации CMS.
   Нестандартные имена БД с пробелами/Unicode намеренно отклоняются.
6. Заменить только файл:
       system/controllers/rx/hooks/cron_bacap_bd.php
   Файл должен иметь обычные права исходника (например 0644), не 0777.
7. В штатном планировщике: компонент rx, хук bacap_bd, например 1440 минут.
   Не создавать вторую такую же задачу. Cron сайта запускается через PHP CLI:
       /usr/bin/php -f /absolute/path/to/site/cron.php example.org
   Это команда всего планировщика сайта, не отдельного PHP-файла хука.
   Не снимать CLI-защиту в cron.php. Новый хук намеренно не работает через HTTP.
8. Первый запуск — на тестовой копии; проверить журнал PHP, SQL, SHA-256,
   восстановление и при необходимости доставку и расшифровку письма.
   После проверки включить задачу на рабочем сайте.

ЧТО ИЗМЕНЕНО
— Никакого shell: proc_open получает массив аргументов.
— Пароль отсутствует в аргументах процесса; временный client.cnf — 0600
  в папке 0700. Значения экранируются по формату option-файла MySQL.
— Ограничены время (600 с), размер дампа (2 GiB), занятое хранилище (10 GiB),
  минимальный остаток диска (512 MiB), stderr (1 MiB).
— Проверяются код завершения, размер и завершающий маркер mysqldump.
  Успешный файл получает итоговое имя только после этих проверок.
— Один запуск на папку через flock. Разным сайтам нужны разные папки.
— Временные файлы удаляются в finally и shutdown; остатки после SIGKILL
  убираются при следующем запуске под блокировкой. При отключении задачи
  удалить .icms-work-* вручную из защищенной папки после остановки процессов.
— Локальный готовый SQL НИКОГДА не удаляется автоматически.
— Почта содержит только проверенный ZIP AES-256. При любой ошибке отправки,
  отсутствии пароля/zip или превышении лимита SQL остается на диске.
— Порог вложения — 10 MiB (и исходного SQL тоже); base64 увеличит письмо.
  Это консервативный лимит, а не попытка сжать большую БД до любого размера.
— Успех send() означает прием почтовым транспортом, а не доставку адресату.
  Готовый SQL остается даже после успешной отправки. Открывать ZIP нужно
  программой с поддержкой AES-256 ZIP (например совместимым архиватором).

ОГРАНИЧЕНИЯ И ОБСЛУЖИВАНИЕ
— Локальный SQL не зашифрован: защищен правами доступа. Пользователь PHP,
  root и скомпрометированный код того же сайта могут его прочитать.
  Для защиты от взлома сервера нужна отдельная удаленная копия с версиями
  и отдельными учетными данными. Загрузки в облако в этом варианте нет.
— Автоудаления старых копий нет намеренно: при лимите задача откажется
  создавать новую. Настроить контроль журнала [rx backup] и заполнения диска;
  удалять старые копии лишь после проверки независимой копии/восстановления.
  Лимиты проверяются раз в 0,2 секунды: возможен небольшой перерасход;
  жесткий лимит обеспечивает квота ОС, а не этот скрипт.
— --single-transaction дает согласованный снимок для InnoDB, при отсутствии
  параллельных ALTER/DROP/RENAME/TRUNCATE. Для MyISAM/Aria и смешанных движков
  этот вариант не гарантирует согласованность: нужен другой режим/окно
  обслуживания. Не выполнять миграции схемы во время копирования.
— Включены routines/events/triggers. Их выгрузка требует соответствующих
  прав БД. Отсутствие прав считается ошибкой; частичный SQL удаляется.
  Для MySQL при ошибке PROCESS/tablespaces может потребоваться осознанное
  добавление --no-tablespaces после проверки используемой структуры БД.
  Не менять кодировку/GTID/TLS-параметры вслепую. Для удаленной БД отдельно
  настроить проверяемое TLS-подключение для вашего клиента MySQL/MariaDB.
— --defaults-file изолирует обычные option-файлы. У MySQL также возможен
  пользовательский .mylogin.cnf: проверить отсутствие конфликтующего login
  path у пользователя cron; MySQL и MariaDB отличаются в этой части.
— Временный конфиг удаляется, но это не гарантированное стирание с SSD,
  файловой системы, swap и снимков хостинга. При SIGKILL/power loss код
  очистки выполнить невозможно; остатки закрыты правами 0700/0600.
— Этот код копирует БД, а не файлы сайта/uploads и не конфигурацию сайта.
— SHA-256 и завершающий маркер — проверка целостности, не тест восстановления.
— Возвращаемое false не следует считать системой уведомлений: cron CMS
  может только обновить дату запуска. Контролировать журнал PHP отдельно.

ПРОВЕРКА ВОССТАНОВЛЕНИЯ
На изолированном тестовом MySQL/MariaDB, без подключения к рабочему сайту:
1. Сверить SHA-256 с файлом .sha256.
2. При использовании почты расшифровать ZIP локально и сверить SHA-256 SQL.
3. Импортировать SQL на тестовом сервере. В дампе есть CREATE DATABASE/USE
   с исходным именем БД: не импортировать его на рабочий сервер для теста!
   Учесть EVENT SCHEDULER: тестовый сервер не должен запускать события с
   внешними побочными эффектами. Проверить таблицы, строки, русский текст,
   emoji при их наличии, триггеры/процедуры и запуск тестовой копии сайта.

ОТКАТ
Отключить задачу, убедиться, что процесс завершился, вернуть исходный PHP.
Готовые SQL сохраняются. Изменений ядра/схемы БД этот хук не делает.

ИСТОЧНИКИ
instantcms.ru/forum/pfind/371434
docs.instantcms.ru/dev/controllers/core
docs.instantcms.ru/manual/settings/scheduler
docs.instantcms.ru/manual/settings/scheduler/task


Прикрепленный файл
instantcms-rx-safe-backup-20261110.zip 11 Кб
#4 10 ноября 2026 в 18:24

🤣 Хаха, теперь и ответы даже нейронки пишут, ну харе уже. Пусть тогда нейронки тут и общаются. Жесть какая то пошла.

#5 10 ноября 2026 в 18:33

Людям уже лень отвечать, копипаста отсюда-туда-обратно. При чём бездумная.

А по сабжу, не знаю что за нейросеть писала этот ужасный код, видимо 2b модель в ollama. Ну скормите вы доки и исходный код InstantCMS, сделайте нормальный rag. Или воспользуйтесь mcp сервером, здесь кто-то выкладывал под InstantCMS. Ну и бэкап по английски будет «backup».

Понимаю, что в связи с ситуацией Beget люди озадачились бэкапами, но бэкапы лучше делать на стороне сервера. Слать на email файлы бэкапа можно конечно, но если база гигабайт например, то что?

#6 10 ноября 2026 в 19:11

Или воспользуйтесь mcp сервером

Fuze

он не очень. лучше доки.

но бэкапы лучше делать на стороне сервера

Fuze

тут или расходы дополнительные на подключение отдельного хранилища. Или проблемы при попытке скачать с сервера гигабайтные архивы (hostiman грешит скачиванием битых бакапов).

Поэтому для себя остановился на связке cwrsync + mysqldump. Ииха пишет прям щас. А взлетит или нет, покажет практика. Но да.

в связи с ситуацией Beget

Fuze

никогда бы не подумал, что такое может произойти с топовым хостером. Поэтому свой бакап ближе к ssd

#7 10 ноября 2026 в 20:25
🤣 Хаха, теперь и ответы даже нейронки пишут, ну харе уже. Пусть тогда нейронки тут и общаются. Жесть какая то пошла.
Clear

я когда писал об этом уже думал)

Добавлено спустя 4 минуты
Или воспользуйтесь mcp сервером Fuze он не очень. лучше доки. но бэкапы лучше делать на стороне сервера Fuze тут или расходы дополнительные на подключение отдельного хранилища.
Zau4man

у меня утро было доброе, бегетик прилёг а там сайты один 2 года ежедневно делал, другой 10 и плюс пачка клиентских сайтов штук 15...

#8 Вчера в 11:19

Поэтому для себя остановился на связке cwrsync + mysqldump. Ииха пишет прям щас. А взлетит или нет, покажет практика.

Zau4man

Отпишитесь пожалуйста получилось ли? так как нужно как то организовать ежедневные копии баз себе на ПК, ну  и файлов может быть, пусть лучше место занимают чем потом бэкапы догонять которых нет)

#9 Вчера в 23:37

Не понимаю проблемы, 300гб ФТП, 300р стоит, столет гоняю более 100гб ежедневно

Вы не можете отвечать в этой теме.
Войдите или зарегистрируйтесь, чтобы писать на форуме.

Похожее в блогах

🍪Мы используем файлы cookie для работы сайта. Читать подробнее.