landing · Сайты 24 26.1600.0 Безопасность

Landing 26.1300.200–26.1600.0: закрыты дыры с PHP и правами групп, поля редактора стали ES-классами

7 мин чтения Устаревших API: 4
Узнавайте о ломающих изменениях Битрикса раньше, чем они доедут до прода: свежие разборы — в канале @bxmax_updates

Обновление безопасности

Закрыта уязвимость или ослабленная проверка прав. Ставить в первую очередь.

С прошлого разбора вышли четыре версии landing: 26.1300.200, 26.1400.0, 26.1500.0 и 26.1600.0. В каждой закрыты дыры, от сборки PHP-тега при импорте сайта до лишних прав у участников групп соцсети. Попутно в режиме Битрикс24 превью черновика переехало на маршрут раздела «Сайты», а поля редактора стали нативными ES-классами. Если у вас свои поля редактора, REST-интеграции с landing или сайты на landing24, прочитайте «Что сломается» до обновления. Предыдущие версии разобраны в Landing 26.1300.0–26.1300.50.

Новое

Решайте сами, кешировать ли динамический блок (26.1500.0)

Перед тем как взять общий HTML-кеш, Block::setDynamic() отправляет событие landing:OnBeforeDynamicBlockCache. В SOURCE_IDS лежат источники данных блока вида module:Source, а получают событие только модули этих источников. Хватит одного EventResult::ERROR, чтобы кеш для этого вывода не использовался. Пригодится, если ваш источник отдаёт выборку, которая зависит от посетителя.

Фильтр сверяет ID модуля обработчика, поэтому регистрируйте его через registerEventHandler(). У обработчика из addEventHandler() ID модуля нет, и событие до него не дойдёт.

        <?php

declare(strict_types=1);

namespace Vendor\Promo\Landing;

use Bitrix\Main\Event;
use Bitrix\Main\EventResult;

final class DynamicCacheGuard
{
    // источники модуля, чья выборка зависит от посетителя
    private const PERSONAL_SOURCES = ['vendor.promo:PersonalOffers'];

    public static function onBeforeDynamicBlockCache(Event $event): EventResult
    {
        $sourceIds = (array)$event->getParameter('SOURCE_IDS');

        $type = array_intersect($sourceIds, self::PERSONAL_SOURCES) === []
            ? EventResult::SUCCESS
            : EventResult::ERROR;

        return new EventResult($type, null, 'vendor.promo');
    }
}

// install/index.php модуля vendor.promo, метод InstallEvents()
\Bitrix\Main\EventManager::getInstance()->registerEventHandler(
    'landing',
    'OnBeforeDynamicBlockCache',
    'vendor.promo',
    \Vendor\Promo\Landing\DynamicCacheGuard::class,
    'onBeforeDynamicBlockCache'
);

    

Выключайте проверку прав так, чтобы она включалась обратно (26.1500.0)

Шаблон $was = Rights::isOn(); Rights::setOff(); … if ($was) Rights::setOn(); ломался тихо. Составной isOn() возвращает false при выключенном глобальном режиме или LANDING_DISABLE_RIGHTS, и локальный флаг так и оставался выключенным. Новый Rights::isLocalOn() читает только локальный флаг, ядро перевело на него и на try/finally около двух десятков мест. Сделайте так же:

        <?php

declare(strict_types=1);

namespace Vendor\Promo\Landing;

use Bitrix\Landing\Rights;

final class WithoutRights
{
    public static function run(callable $action): mixed
    {
        $wasOn = Rights::isLocalOn();
        Rights::setOff();

        try {
            return $action();
        } finally {
            if ($wasOn) {
                Rights::setOn();
            }
        }
    }
}

    

Новый Hook\Page\Cookies::getSelectedAgreementIdBySiteId() из 26.1600.0 снова вызывает setOff() и setOn() без запоминания состояния, то есть по схеме, которую вычищали в 26.1500.0.

Свой пункт в настройках сайта (26.1400.0)

Встройка LANDING_SETTINGS появилась в описании REST-скоупа landing, и её можно зарегистрировать через placement.bind. Пункт появится в боковом меню настроек сайта, приложение получит в опциях встройки SITE_ID и LID. Для сайтов VIBE пункты не выводятся.

Разбирайте чужой HTML своим парсером (26.1400.0)

new DOM\Document() из main берёт один парсер на процесс. С storePhpCode он прячет <? … ?> в комментарий-плейсхолдер и помнит исходник до конца процесса, так что другой документ с тем же комментарием получит этот PHP обратно (mantis 0257122). Landing\Html\DocumentFactory::createForUserHtml() создаёт документ с отдельным HtmlParser и выключенным storePhpCode. В своём модуле повторите приём: new DOM\HtmlParser(), storePhpCode = false, $document->setParser().

Доступные блоки без JS (26.1400.0)

Опция манифеста 'ariaLabelFromHref' => true у узла типа link подставляет href ссылки в её aria-label, так сделаны 15.social и 15.2.social_circles. HTML-блок на уровне санитайзера LOW пропускает семантические теги (nav, main, section, article и другие), role из белого списка и основные aria-*. Обработчики событий и script по-прежнему запрещены.

Для пользователей портала

  • У Vibe (главной страницы портала) появились внутренние страницы по адресу /pub/site/vibe/{siteId}/{путь}, их видят авторизованные пользователи с доступом к Vibe. Шаблон news-detail доступен типу vibe (26.1600.0).
  • Новая CRM-форма из редактора сайта получает согласие, которое подтверждается отправкой формы, если это поддерживает модуль crm (26.1600.0).
  • Экспорт сайта перечисляет только приложения repo-блоков, и при импорте лишнего ставить не попросят (26.1500.0).

Что сломается

Превью черновика в режиме Битрикс24 (26.1300.200)

Если установлен bitrix24, crm или intranet, не задана LANDING_DISABLE_CLOUD и сайт в публичном скоупе, Landing::getPublicUrl() в режиме превью отдаёт /sites/site/<siteId>/preview/<landingId>/ вместо /pub/site/<id>/preview/<hash>/…. Такое превью требует авторизации и права на чтение страницы, а розданные подписанные ссылки черновик больше не показывают. На БУС без этих модулей всё по-старому. Ссылку на превью берите из Landing::createInstance(0)->getPublicUrl($landingId) при включённом Landing::setPreviewMode(true), потом верните прежний режим. Site\PortalPreview::buildUrl() условий режима не проверяет, и на БУС его ссылка ведёт на несуществующий маршрут.

REST и методы страниц

  • PublicAction\AiBlock удалён (26.1400.0). Его createTemplate() и addToLanding() были доступны через ajax-шлюз landing, замены в публичном API нет.
  • landing.landing.addByTemplate через REST и ajax игнорирует PREPARE_BLOCKS и PREPARE_BLOCKS_DATA (26.1400.0). Из PHP поля принимаются, но в параметрах компонентов нескалярные значения и значения, начинающиеся с ={, пропускаются. Параметры компонентов меняйте вторым шагом через landing.block.updateNodes (ключ ноды вида bitrix:search.title, значение {"attrs": {…}}). Пройдут только параметры из extra манифеста и только у не-repo блоков.
  • landing.repo.register и landing.repo.unregister без контекста приложения требуют администратора портала либо роль сайтов с «Полным доступом», если тариф даёт расширенные права (26.1500.0). Вебхук обычного пользователя получит ACCESS_DENIED. Выпустите его от администратора или вызывайте метод из приложения.
  • landing.landing.update проверяет canEdit() самой страницы, прав на сайт уже мало (26.1500.0).
  • Landing::delete() проверяет canDelete() страницы и блокировку до удаления блоков (26.1600.0). landing.landing.delete теперь удаляет и страницы из корзины или удалённого сайта. Если ваша интеграция ждала тут ошибку, проверьте её.
  • Vibe\Vibe::delete() объявлен с : Result (26.1400.0). Наследник, переопределивший метод без типа, упадёт с fatal error. Допишите : \Bitrix\Main\Result и возвращайте объект результата.
  • Node\Component::saveNode() получил array $additional = [] и раскодирует строковые параметры, кроме html, через htmlspecialchars_decode() (26.1600.0). Отключается это через ['decodeValues' => false]. Наследникам нужен новый параметр в сигнатуре.

Права на сайты групп (26.1600.0)

Rights::getOperations() прогонял карту операций группы ['read' => bool, 'edit' => bool, …] через array_values(), а hasAccessForSite() искал в ней нестрогим in_array(). В PHP 'delete' == true, и одно разрешённое действие открывало все остальные. Теперь сравнение строгое. Участники групп, которые с одним правом настраивали, публиковали или удаляли сайт группы, этого больше не смогут.

Поля редактора на ES-классах (26.1500.0)

BX.Landing.UI.Field.Text и ещё несколько полей собраны без Babel, и вызов BX.Landing.UI.Field.Text.apply(this, arguments) бросает TypeError. Ядро наступило на это само. По коду в 26.1500.0 не создаются поле видео-вставки и поля Unit. Их починили в 26.1600.0 через Reflect.construct, так что с 26.1500.0 обновляйтесь сразу дальше. Свои поля переведите на class extends или на тот же приём:

        class VendorPhoneField extends BX.Landing.UI.Field.Text
{
	constructor(data)
	{
		super({ ...data, textOnly: true });
		BX.Dom.addClass(this.layout, 'vendor-phone-field');
	}
}

// если нужна старая функция-конструктор
BX.Landing.UI.Field.VendorPhone = function(data)
{
	var self = Reflect.construct(BX.Landing.UI.Field.Text, [data], new.target || BX.Landing.UI.Field.VendorPhone);
	self.mask = '+7 (___) ___-__-__';

	return self;
};
BX.Landing.UI.Field.VendorPhone.prototype = Object.create(BX.Landing.UI.Field.Text.prototype);
BX.Landing.UI.Field.VendorPhone.prototype.constructor = BX.Landing.UI.Field.VendorPhone;

    

Шаблон landing24 и путь vibe

  • landing24 (26.1400.0) больше не запрещает масштабирование во viewport, первой в <body> выводит ссылку «Перейти к содержимому» и рисует кольцо фокуса :focus-visible. Сайты со своим CSS фокуса или копией шаблона проверьте. С 26.1500.0 хлебные крошки обёрнуты в <nav>, и селектор по прямому родителю ul.landing-breadcrumb может отвалиться.
  • На портале с bitrix24, crm или intranet landing.pub разбирает путь vibe и vibe/… как маршрут Vibe и при неудаче отдаёт 404 (26.1600.0). В этом режиме первый сегмент пути означает сайт, поэтому по коду перестанет открываться сайт с кодом vibe по адресу /pub/site/vibe/…. Ссылайтесь на него по ID или смените код.

Что закрыли

  • Импорт сайта (26.1300.200, mantis 257039): подменённые id блоков в архиве собирали php-тег из санитизированного контента. С 26.1500.0 такой блок пропускается, а в лог уходит LANDING_IMPORT_PHP_TAG.
  • Общий DOM-парсер (26.1400.0, mantis 0257122) и подстановка #TITLE# в PHP-параметр компонента. UrlChecker при сбое внешнего сервиса больше не записывает ссылку чистой навсегда.
  • Подсветка поиска в landing.pub (26.1500.0) вставляла фразы из q в регулярку без preg_quote, а контент не-repo блоков потом исполняется через eval.
  • Права: настройки чужой страницы в landing.landing_edit (26.1500.0). Остальные проверки прав описаны в «Что сломается».

Deprecated

  • File::markAssetRebuilded() заменён на File::markAssetRebuilt(), старый метод вызывает новый (26.1400.0).
  • Assets\ResourceCollection::getPathes() заменён на getPaths(), а параметр $pathes в remove() стал $paths. Вызовы с именованным аргументом сломаются (26.1400.0).

Мелочи и находки

  • Site\PreviewUrl прожил две версии и удалён в 26.1300.200. Класс был final и использовался только внутри модуля, getPortalOrigin() и withRequestPort() переехали в Site\PortalPreview с прежними сигнатурами.
  • Ключ фразы LANDING_HOOK_SPEED_TTILE исправлен на LANDING_HOOK_SPEED_TITLE (26.1400.0), переопределение старого ключа в своих lang-файлах перестанет действовать.
  • landing.start в SEF-режиме при первом обращении сам добавляет в b_site_template строку с условием на маршрут превью (26.1300.200).

Устаревшие и удалённые API в этой версии

Символ Статус Чем заменять
Bitrix\Landing\File::markAssetRebuilded() Deprecated Bitrix\Landing\File::markAssetRebuilt()
Bitrix\Landing\Assets\ResourceCollection::getPathes() Deprecated Bitrix\Landing\Assets\ResourceCollection::getPaths()
Bitrix\Landing\Site\PreviewUrl Удалено Bitrix\Landing\Site\PortalPreview
Bitrix\Landing\PublicAction\AiBlock Удалено —

Новая версия вышла — разбор уже в Telegram

В канале @bxmax_updates публикуем каждый свежий разбор: что сломается, что устарело и что появилось в API. Узнаете до того, как обновите прод.

Подписаться

Читайте дальше

landing 26.1300.50 Безопасность Свежее

Landing 26.1300.0–26.1300.50: публикация больше не склеит PHP-тег, сайт по описанию через REST, а note меняет ответы REST v3

Вышли landing 26.1300.0 и 26.1300.50, вместе с первой пришёл note 26.500.0. Note на БУС нет, его смотрели на коробочном Битрикс24. В landing закрыли дыру в публикации блоков и добавили REST-методы, которые собирают сайт из текстового описания. Note научился забирать к себе Базу знаний 1.0 и поменял…

2 мин
landing 26.1100.0 Безопасность

Landing 26.1100.0: закрыта цепочка уязвимостей, заодно ужесточены repo.register, savePicture и проверка ссылок предпросмотра

`landing` 26.1100.0 (сборка от 06.08.2026) почти целиком про безопасность. Закрыты исполнение кода через манифест repo-блока и импорт архива сайта, инъекция в `eval()` через CODE страницы, SSRF через обработчик виджета, XSS в выводе ассетов, предсказуемый в облаке md5-хеш предпросмотра. В комментари…

14 мин
landing 26.1200.0 Безопасность

Landing 26.1200.0: подписанный предпросмотр ушёл в CSP-песочницу, а импорт с заменой сайта помечает старые страницы удалёнными

Landing 26.1200.0 трогает 690 файлов, но основная масса там картинки, переводы и пересобранные бандлы. Релиз закрывает дыры в подписанной ссылке предпросмотра, чинит импорт с заменой сайта и размечает для скринридеров интерфейсы прав, настроек и редактора. Если вы вызываете `AiSiteChatAvailabilitySe…

2 мин
landing 26.1100.100 Рутинное

Landing 26.1100.100: только номер версии

В `install/version.php` поменялись номер версии и дата сборки (20 августа), больше в релизе ничего нет. Следом идёт [landing 26.1100.200](/bitrix-updates/landing/landing-26-1100-200) с содержательной правкой контроллера файлов Диска.

1 мин
Мы используем файлы cookie для улучшения работы сайта. Продолжая использовать сайт, вы соглашаетесь с нашей политикой конфиденциальности.
AI Домовой

AI Домовой История

на связи

пишет…

Можно закрыть чат и перейти на другую страницу. Когда ответ будет готов, на кнопке Домового появится отметка.

Нет истории чатов
AI Домовой

Нужна авторизация

Войдите, чтобы задавать вопросы AI Домовому.

Войти