Безопасность

Модуль даёт другим модулям заготовки для синхронизаций, и самые опасные места у него общие с ними: кто может запускать агенты и импорт, что ложится в каталоги импорта, что уходит в SQL и в лог. Ниже — что держит модуль и что остаётся проекту.

Модуль даёт другим модулям заготовки для синхронизаций, и самые опасные места у него общие с ними: кто может запускать агенты и импорт, что ложится в каталоги импорта, что уходит в SQL и в лог. Ниже — что держит модуль и что остаётся проекту.

Кто управляет импортом

\Shef\InSync\Main\Access::canManage() — одно правило на весь модуль:

  • администратор портала — да;
  • пользователь с правом «Запись» (W) и выше на модуль импорта в «Настройки → Настройки продукта → Права доступа» — да;
  • остальные, включая гостя, — нет.

Модуль импорта — тот, чей агент или класс импорта: права на shef.demosync открывают его импорт, но не агенты main. Трогать из интерфейса модуля можно только агенты импорта — наследники \Shef\InSync\Agents\AAgent: право «W» на sale или crm не открывает их штатные агенты, это остаётся администратору в списке агентов ядра. Для страницы статистики и перехода к таблице импорта — права на shef.insync.

Проверяется и на показ, и в действии: адрес ajax-действия виден в коде страницы и вызывается напрямую. До 2.0.0 все действия модуля стояли на Actions\Normal — это умолчания ядра, вход на портал и csrf, — и любой вошедший сотрудник:

  • включал и выключал любой агент портала по ID — и из компонента статистики, и из контроллера страницы настроек;
  • загружал файлы в импорт и прогонял агент импорта;
  • чистил таблицу импорта.
гдечто проверяется
левое меню (Integration\Intranet\CustomSectionProvider)страницы модуля видны только тем, кому разрешены
shef.insync:import.stat.localстраница и все действия — права на shef.insync; кнопки агента — права на модуль агента
shef.insync:import.from.fileстраница и действия — права на модуль импорта, до того как подключается модуль и создаётся объект импорта
Main\Options\Agent\Controllerагент существует, принадлежит модулю из запроса, права на этот модуль
Main\Options\Import\FromFile\AControllerправа на модуль контроллера (из его namespace)

Сторожит tests/access_test.php.

Каталоги импорта — вне корня сайта

Каталог импорта — \Shef\InSync\Main\Constants::getImportDir(). По умолчанию он на уровень выше корня сайта:

корень сайтакаталог импорта
/home/bitrix/www (BitrixVM)/home/bitrix/sh_import
/var/www/portal/var/www/sh_import

Внутри: <код>/ — файлы на импорт, copy/<код>/ — архив, problem/<код>/ — файлы с проблемой (Sync\FromFile\AFileProcess::getImportFolder(), getDoneFolder(), getProblemFolder()).

В выгрузках — цены, клиенты, заказы. Вне корня сайта веб-сервер их не отдаёт, настраивать для этого ничего не нужно. До 2.0.0 каталог был /upload/import, под корнем сайта, а имена архива — из кода импорта и даты с точностью до минуты: перебором за срок хранения выгрузку скачивал кто угодно.

Свой каталог

Проект задаёт каталог в /bitrix/.settings_extra.php:

return [
    'shef.insync' => [
        'value' => [
            'importDir' => '/var/data/import',
        ],
        'readonly' => true,
    ],
];

Принимается только абсолютный путь. Относительный зависел бы от текущего каталога процесса: агент из cron искал бы файлы не там, куда их положила страница загрузки. Что-то кроме абсолютного пути — каталог по умолчанию. Каталог обязан быть вне корня сайта — модуль это не проверяет, это решение проекта.

Модуль-импорт, которому нужен свой путь, по-прежнему может переопределить getImportFolder() и соседей.

Права и open_basedir

Каталоги создаются сами при первом импорте — если пользователь PHP может писать в родителя. На BitrixVM /home/bitrix принадлежит bitrix, всё работает из коробки. В другом окружении создайте каталог заранее:

sudo mkdir /var/www/sh_import && sudo chown www-data: /var/www/sh_import

Если в PHP задан open_basedir, каталог импорта должен в него входить.

Внешний обмен (1С, FTP, rsync), который кладёт файлы, пишет теперь сюда, а не в /upload/import/<код>/: пользователю обмена нужен доступ на запись в <каталог импорта>/<код>/.

После обновления с 1.x

Старый /upload/import модуль не трогает: в нём ваши данные, и он по-прежнему открыт веб-серверу. Перенесите нужное, перенастройте обмены на новый каталог и удалите старый:

mkdir -p /home/bitrix/sh_import
cp -a /home/bitrix/www/upload/import/. /home/bitrix/sh_import/
# перенастроить обмены на /home/bitrix/sh_import/<код>/
rm -r /home/bitrix/www/upload/import

Что ещё держит модуль

  • имя загружаемого файла проверяет ShefInSyncImportFromFileComponent::prepareUploadName(): только имя без пути, без скрытых файлов и исполняемых расширений (.php, .phtml, .phar, .htaccess, .html, .svg, .js и прочие, плюс список ядра HasScriptExtension()), и если импорт объявил getImportFileAccept() с расширениями — только с ними. До 2.0.0 имя от браузера шло в путь как есть;
  • имена архивных файлов не угадать: done_<код>_<дата>_<16 случайных символов>.<расш> — вторая линия на случай, если проект задаст свой каталог под корнем сайта.

SQL

Sync\Model\SyncCollection собирает запросы к таблице импорта сам. Значения — код импорта, дата загрузки — экранируются через SqlHelper::forSql() (SyncCollection::buildWhere()). До 2.0.0 код импорта вставлялся в запрос как есть, а в clear() он приходит параметром ajax-запроса компонента статистики: SQL-инъекция для любого вошедшего. Сторожит tests/synccollection_test.php.

Строка агента

Ядро исполняет строку агента из b_agent как PHP-код. Agents\Entity::prepareNameForDb() экранирует параметры var_export(): кавычка в значении не ломает агент и не становится кодом. Параметры — только строки и числа. Сторожит tests/agents_test.php.

Вывод

Всё, что приходит из данных — строки файла, ответы API, имена агентов из b_agent, код импорта в гриде, — экранируется перед выводом. Описания импорта и агента (getProcessDescription(), Entity::getDescription()) выводятся как разметка: их пишет разработчик класса, а не пользователь.

Логи

Api\AConnector при ошибке пишет в лог отправленный запрос. Заголовки с authorization, token, key, secret, password, cookie, session в имени уходят туда маской (Api\Headers::mask()). Параметры запроса пишутся как есть — не кладите секреты в параметры, передавайте их заголовком.

Сами логи — модуля shef.problems, вне корня сайта, см. его security.md.

XML

Разбор идёт через XMLReader без LIBXML_NOENT: внешние сущности не раскрываются (XXE). Сторожит tests/vendor_test.php.

← Опции настроек модуля | ↑ Содержание

Источник: insync/docs/security.md — правки туда, сайт пересобирается сам.
CtrlI