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

Disk 26.1100.0: закрыты дыры в правах на публичные ссылки и версии, единые ссылки больше не выключаются

4 мин чтения Устаревших API: 4

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

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

Disk 26.1100.0 вышел в коробку 2 сентября 2026 года (по официальному каналу «Битрикс24 changelog») и закрывает три дыры в правах. Любой, кто видел файл, мог поставить или снять пароль и срок действия его публичной ссылки, подпись файла _uls открывала всю историю его версий, а вложения к комментариям встреч читал кто угодно. Ещё модуль включил единые ссылки безусловно. Опции unified_link.enabled и unified_link.allow_type_* больше не читаются, так что портал, где они не стояли в Y, после обновления единые ссылки получит.

Разработчикам придётся проверить наследников Folder, DownloadController и CheckReadPermission, позиционные вызовы в Internal\Service\UnifiedLink и скрипты, которые удаляют папки. Сборка датирована 4 августа 2026 года, дифф от 26.1000.0 затрагивает 496 файлов, +29297/−10271 строк, причём JS и компоненты посчитаны дважды: в bitrix/js и bitrix/components и ещё раз в install/.

Какие дыры закрыты

Настройки публичной ссылки больше не меняются с одним правом чтения

Контроллер Controller\ExternalLink проверял права префильтром-замыканием, которое требовало на объекте только canRead. Поэтому setPasswordAction, setDeathTimeAction, revokePasswordAction, revokeDeathTimeAction и disallowEditDocumentAction мог вызвать любой, кто видит файл, а canUpdate проверялся только внутри allowEditDocumentAction.

Замыкание заменили фильтром Internals\Engine\ActionFilter\CheckExternalLinkSettingsPermission. Он требует BaseObject::canManageExternalLink(), то есть чтение плюс canUpdate или включённую у объекта опцию «управлять публичным доступом при чтении». Для allowEditDocument и disallowEditDocument нужен именно canUpdate. Права проверяются на реальном объекте (getRealObject()).

Все экшены контроллера заодно получили типы возврата. Класс объявлен final, так что наследников у него быть не может.

Подпись файла больше не открывает его версии

DownloadController::checkPermissions() принимал подпись _uls, выданную на файл, и по versionId отдавал любую версию этого файла. По комментарию в коде, подпись файла отдаётся в JSON каждого файла, так что вместе с документом открывалась вся его история. Теперь для доступа к версии по _uls нужна подпись пары «файл + версия» (hasUnifiedLinkAccess()). Такие подписи выдаёт и проверяет новый Internal\Service\UnifiedLink\UnifiedLinkSignature.

Фильтр Internals\Engine\ActionFilter\CheckReadPermission пропускал версию, у которой нет файла-владельца (continue). В 26.1100.0 checkVersion() считает это ошибкой чтения, а для версии фильтр принимает _uls пары. Битая подпись вместо BadSignatureException даёт обычный отказ.

Если ваш код строил ссылки на версии с подписью самого файла, к версиям эта подпись больше не подходит.

Вложения встреч требуют роли во встрече

Uf\ForumMessageConnector::canRead() кешировал результат одним значением на объект коннектора, и этот ответ получал любой следующий $userId. Теперь кеш хранится отдельно для каждого пользователя.

Вложения к комментариям встреч (MEETING, MEETING_ITEM) раньше читал кто угодно, если сообщение лежало в форуме комментариев встреч (meeting:comments_forum_id). Теперь для чтения нужна роль во встрече, её определяет CMeeting::getUserRole. canUpdate() этого коннектора вызывает canRead(), поэтому и правка таких вложений требует той же роли.

Единые ссылки больше не выключаются

В Internal\Service\UnifiedLink\Configuration методы isEnabled() и isFileTypeAllowed() теперь всегда возвращают true, а supportsUnifiedLink() проверяет только наличие UNIQUE_CODE. Опции unified_link.enabled и unified_link.allow_type_2…unified_link.allow_type_12 модуль больше не читает, install/index.php их тоже не выставляет. Раньше модуль считал выключенной любую из этих опций, кроме Y, в том числе незаданную. Если на портале они не равны Y, после обновления единые ссылки включатся для всех типов файлов, и этими опциями их уже не выключить.

Посмотреть, какие из этих опций выключены у вас, можно до обновления:

        <?php declare(strict_types=1);

use Bitrix\Main\Config\Option;

$names = ['unified_link.enabled'];
foreach (range(2, 12) as $type) {
    $names[] = 'unified_link.allow_type_' . $type;
}

// До 26.1100.0 выключенной считалась любая опция, кроме 'Y', в том числе незаданная
$switchedOff = array_values(array_filter(
    $names,
    static fn (string $name): bool => Option::get('disk', $name, 'N') !== 'Y',
));

print_r($switchedOff);

    

Непустой список значит, что после обновления эти настройки перестанут действовать.

У FileHandler\HtmlRenderableFileHandlerFactory::createHandler() удалили шестой параметр bool $forceExternal. На его место встал bool $deferred, а в конец добавился ?ExternalLink $externalLink. Позиционный вызов, который передавал forceExternal, теперь включает отложенную загрузку. Анонимный посетитель, для которого внешнюю ссылку не передали, получает DefaultHtmlRenderableFileHandler, а не ExternalLinkHandler. Передавайте аргументы по имени и отдавайте фабрике саму публичную ссылку:

        <?php declare(strict_types=1);

// $factory: HtmlRenderableFileHandlerFactory, $link: Bitrix\Disk\ExternalLink
$handler = $factory->createHandler(
    file: $file,
    deferred: false,
    externalLink: $link,
);

    

Конструктор FileHandler\ExternalLinkHandler теперь требует ссылку вторым параметром:

        __construct(File $file, ExternalLink $externalLink, ?AttachedObject $attachedObject = null, ?Version $version = null)

    

Старый вызов new ExternalLinkHandler($file) упадёт с ArgumentCountError.

Папку почтовых вложений удалит только система

Папку с кодом FOR_MAIL_ATTACHMENTS и любое дерево, где она лежит, удаляет или отправляет в корзину только системный пользователь. Для остальных Folder::canDelete() и canMarkDeleted() возвращают false, а удаление и пометка удаления заканчиваются ошибкой DISK_FOLDER_22001. У методов удаления появился параметр $bypassDeletionRestriction:

        Folder::deleteTree($deletedBy, bool $bypassDeletionRestriction = false)
Folder::deleteNonRecursive($deletedBy, bool $bypassDeletionRestriction = false)
FolderLink::deleteTree($deletedBy, bool $bypassDeletionRestriction = false)
Storage::delete($deletedBy, bool $bypassDeletionRestriction = false)

    

Наследник Folder со старой сигнатурой deleteTree() или deleteNonRecursive() с новым родителем несовместим. SocialnetworkHandlers::onUserDelete() удаляет хранилище пользователя с bypassDeletionRestriction: true. Если ваш код удаляет хранилища не от имени системного пользователя, передавайте тот же флаг.

Ещё deleteTree() и markDeleted() останавливаются на первом потомке, которого не удалось удалить, и поднимают его ошибки наверх. Раньше цикл шёл дальше. Скрипт очистки больше не удалит «всё, что получится», поэтому проверяйте результат:

        <?php declare(strict_types=1);

use Bitrix\Disk\Folder;
use Bitrix\Main\Loader;

Loader::requireModule('disk');

$folder = Folder::loadById($folderId);

if ($folder !== null && !$folder->deleteTree($userId)) {
    $logger->warning('Папка удалена не полностью', [
        'folderId' => $folderId,
        'mailAttachmentsInside' => $folder->getErrorByCode('DISK_FOLDER_22001') !== null,
        'errors' => array_map(static fn ($error) => $error->getMessage(), $folder->getErrors()),
    ]);
}

    

Найдите в своём коде $this->signer и ERROR_NO_AI_MODULE

Из DownloadController и Internals\Engine\ActionFilter\CheckReadPermission удалили свойство protected Signer $signer, вместо него появилось protected UnifiedLinkSignature $unifiedLinkSignature. Наследник, который обращался к $this->signer, сломается.

В Internal\Service\MarkdownRenderService удалена константа ERROR_NO_AI_MODULE, вместо неё ERROR_RENDERER_UNAVAILABLE с кодом DISK_MARKDOWN_RENDER_UNAVAILABLE. Parsedown теперь берётся из модуля ui (ui/vendor/erusev/parsedown), а не из ai, и Configuration::isEnabledMarkdownViewer() проверяет MarkdownRenderService::isAvailable() вместо Loader::includeModule('ai'). RENDER_FORMAT_VERSION поднят с 2 до 3, так что старый кеш рендера сбросится. Санитайзер Markdown теперь пропускает <em>.

Эти обращения и вызовы обработчиков единых ссылок ищутся одной командой из корня сайта:

        grep -rnE '\$this->signer|ERROR_NO_AI_MODULE|new ExternalLinkHandler\(|->createHandler\(|function (deleteTree|deleteNonRecursive)\(' local/

    

Новое

HTML-просмотрщик приехал выключенным

В модуле появился просмотрщик HTML-файлов, по умолчанию выключенный. Он состоит из нескольких частей:

  • экшены showHtmlAction() в Controller\File, Controller\Version и Controller\AttachedObject с теми же префильтрами, что у download: без CSRF, с Authentication(true) и CloseSession;
  • сервисы в lib/Internal/Service/: HtmlViewerService отдаёт файл изолированным документом, HtmlViewerPolicy решает, что открывать (html и htm с TypeFile::KNOWN), рядом HtmlViewerPageService и HtmlViewerSharingPolicy;
  • адреса строят UrlManager::getUrlForShowHtml(), getUrlForShowHtmlAttached() и getUrlForShowHtmlVersion();
  • компонент disk.file.viewer-html, рендерер UI\Viewer\Renderer\Html, обработчик FileHandler\HtmlFileHandler, FileExtensionMap::Html и префикс единой ссылки UnifiedLinkPrefix::Html (/html/);
  • трейт Infrastructure\Controller\HtmlViewerRefusalResponse, с ним отказ фильтра приходит HTML-заглушкой, а не JSON.

Настройки лежат в lib/configuration.php: isEnabledHtmlViewer() (опция disk_enable_html_viewer, по умолчанию N), getMaxSizeForHtmlViewer() (disk_html_viewer_max_size, 10 МБ по умолчанию) и getHtmlViewerAllowedHosts() (disk_html_viewer_csp_hosts).

Санитайзера у просмотрщика нет, HtmlViewerService отдаёт HTML байт в байт. Изолируют документ заголовки. Content-Security-Policy начинается с sandbox allow-scripts, за ним идут default-src 'none', connect-src 'none', form-action 'none', frame-ancestors 'self' и webrtc 'block'. По умолчанию из внешних источников разрешены только шрифты и стили Google Fonts, хосты из disk_html_viewer_csp_hosts добавляются к style-src, font-src и img-src. Iframe в шаблоне disk.file.viewer-html тоже получил sandbox="allow-scripts". Хосты из disk_html_viewer_csp_hosts проходят проверку #^https://[a-z0-9.-]+$#, поэтому дописать через опцию свою директиву в заголовок не получится.

Документ, переименованный в .html, как text/html не отдаётся. Тип файла вычисляется при загрузке и при переименовании не пересчитывается, так что у такого файла остаётся TypeFile::DOCUMENT, и HtmlViewerPolicy его не пропустит.

Пока просмотрщик выключен, UnifiedLinkPrefix::forHtml() оставляет HTML-файлы на префиксе /file/. Маршрут /html/ живёт в модуле intranet, и портал, где обновили только disk, отвечал бы на такие ссылки 404. В эталоне маршрут уже есть в intranet 26.1350.0 (html в шаблоне fileType в intranet/install/routes/intranet.php). Прежде чем включать опцию, убедитесь, что intranet на портале знает маршрут /html/. Исключение /html/ из редиректа экстранет-пользователей разобрано отдельно, в extranet 26.300.0.

По публичной ссылке HTML-файл открывается в том же просмотрщике, только без окна настройки доступа.

Настройки публичной ссылки можно заблокировать

У публичной ссылки появился флаг CAN_EDIT_SETTINGS (колонка в b_disk_external_link, по умолчанию 1) и методы ExternalLink::canEditSettings() и getCanEditSettings(). Если флаг снят, changePassword(), changeDeathTime(), revokePassword(), revokeDeathTime(), changeAccessRight() и changeCanDownloadWithReadAccess() возвращают false с ошибкой DISK_EL_22001 (ExternalLink::ERROR_SETTINGS_EDIT_DENIED).

Для объектов из папки почтовых вложений BaseObject::addExternalLink() всегда пишет CAN_EDIT_SETTINGS = false, что бы ни прислал клиент. Controller\AccessRights не даёт поменять canEditSettings у существующей ссылки и отклоняет правку настроек заблокированной. При создании ссылки права, пароль, TTL и canDownloadWithReadAccess теперь применяются в том же запросе.

Код, который меняет настройки ссылок, может отличить блокировку от прочих сбоев по коду ошибки:

        <?php declare(strict_types=1);

use Bitrix\Disk\ExternalLink;
use Psr\Log\LoggerInterface;

function setLinkPassword(ExternalLink $link, string $password, LoggerInterface $logger): bool
{
    if ($link->changePassword($password)) {
        return true;
    }

    $logger->warning('Пароль публичной ссылки не изменён', [
        'linkId' => $link->getId(),
        'settingsLocked' => $link->getErrorByCode(ExternalLink::ERROR_SETTINGS_EDIT_DENIED) !== null,
    ]);

    return false;
}

    

В своём интерфейсе canEditSettings() поможет заранее спрятать форму пароля и срока.

Пароль публичной ссылки теперь проверяется без перезагрузки страницы. В install/components/bitrix/disk.external.link/ajax.php появился DiskExternalLinkAjaxController::checkPasswordAction($hash, $password) с фильтрами POST и CSRF, авторизация ему не нужна. Подтверждение пароля хранится 24 часа в ExternalLinkPasswordService.

disk.file.search научился фильтровать по типу файла

REST-метод disk.file.search принимает фильтр FILTER[FILE_TYPE], но только при TYPE=file. Значение передаётся строкой или списком из document, image, video, audio, archive, pdf, vector_image, board, known и unknown. Добавлены ошибки SearchError::invalidFileType() и incompatibleFileType(). На стороне PHP у Search\StorageFileFinderOptions появился параметр ?array $typeFileValues.

У почтовых вложений своя папка

В SpecificFolder появились код CODE_FOR_MAIL_ATTACHMENTS = 'FOR_MAIL_ATTACHMENTS' и проверки isSpecificFolder() и isSpecificFolderCode(), в Storage метод getFolderForMailAttachments(). Запрет удаления собран из правил: сервис Internal\Service\Folder\DeletionRestriction, интерфейс DeletionRestrictionRule и правило MailAttachmentsFolderDeletionRestrictionRule.

Модуль mail в эталоне уже пользуется этой папкой. mail/lib/integration/disk/reallargeattachmentstorage.php вызывает getFolderForMailAttachments(), а largeattachmentstoragefactory.php страхуется method_exists() на случай старого disk.

Что ещё добавили

  • Vibeoffice ведёт список тех, кто сейчас в документе. Document\Vibeoffice\Service\PresenceService и PresenceAccessService обрабатывают enter, heartbeat и leave, регистрация живёт 90 секунд, лимиты 8 на сессию, 16 на пользователя и 100 на область. SavedContentSynchronizer дожидается сохранённой версии перед открытием просмотра, Controller\Vibeoffice::waitForSavedAction() принимает POST с JSON. В DocumentSessionManager добавлен cloneSessionWithCurrentContentVersion(), persistContentVersionSnapshot() получил параметр ?File $file = null, а findOrCreateSession() перечитывает файл перед поиском сессии. У DocumentSession появились getExternalLinkId() и matchesExternalContext().
  • Для бизнес-процессов появился триггер DiskFileCreateTrigger (install/activities/bitrix/diskfilecreatetrigger/), ему нужен CBPRuntime::ACTIVITY_API_VERSION >= 4. Рядом Integration\Bizproc\Starter\DiskModuleSettings и BizProcDocument::getStarterModuleSettings().
  • Bitrix24Disk\TreePathResolver (@internal) строит пути для снапшотов десктопного клиента по b_disk_object_path и не собирает дерево в памяти. По докблоку, класс на 827 строк заменяет тяжёлый JOIN трёх таблиц с EXISTS-проверкой прав, который выполнялся при каждом снапшоте под нагрузкой. Включается опцией snapshot_path_resolver_enabled, по умолчанию N.
  • Internal\Service\UnifiedLink\ExternalLinkContext::create() и resolve() работают с подписанным маркером externalLinkContext, по нему единая ссылка открывается в контексте публичной. UnifiedLinkAccessService::check() и AccessCheckHandlerFactory::create() получили необязательные ?ExternalLink и ?Version.
  • По мелочи: User::getExternalAuthId(), Ui\Avatar::preload(), Ui\Icon::getIconSetNameByFile() и getIconSetNameByObject(), FileAttributes::buildByFileId(..., ?Version $version = null), FileResolver::resolveVersion(), getHeaders() у RenderResult и FileHandlerOperationResult, JS-расширение disk.board-create, шаблон standalone у disk.error.page и шаблон air у disk.breadcrumbs.

БД

Схема описана в install/migrations/tables.php:

  • b_disk_external_link: новая колонка CAN_EDIT_SETTINGS tinyint NOT NULL DEFAULT 1, в ORM Internals\ExternalLinkTable это boolean со значением по умолчанию true;
  • b_disk_document_session: колонка EXTERNAL_LINK_ID int и индекс IX_DISK_DS_EXTERNAL_LINK_ID (EXTERNAL_LINK_ID, ID), в DocumentSessionTable добавлено IntegerField('EXTERNAL_LINK_ID'). Если поле не передали, DocumentSession::add() берёт его из CONTEXT.

install/db/mysql/install.sql и install/db/pgsql/install.sql не менялись, модуль ставится через installMigrations() в install/index.php. Как колонки появятся на уже работающих порталах, по диффу не видно, апдейтер в снапшот не попадает.

Новые опции: disk_enable_html_viewer, disk_html_viewer_max_size, disk_html_viewer_csp_hosts, snapshot_path_resolver_enabled. Из install/index.php убраны Option::set для unified_link.enabled и unified_link.allow_type_*.

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

  • SocialnetworkHandlers::onAfterUserUpdate() больше не переименовывает хранилища внешних пользователей (EXTERNAL_AUTH_ID из UserTable::getExternalUserTypes()). По комментарию в коде, открытые линии переписывают имя контакта при каждом входящем сообщении.
  • Controller\AttachedObject::configureActions() теперь начинается с parent::configureActions(). Раньше метод возвращал только download и showMarkdown.
  • Folder::processAdd() ловит DuplicateEntryException вместо поиска (1062) в тексте ошибки. На PostgreSQL подбор уникального имени обёрнут в транзакцию.
  • Публичную страницу ссылки (install/components/bitrix/disk.external.link/) переписали, появились шаблоны access-card, folder-list и file-viewers/html. Номер страницы в списке папки теперь ограничен сверху. Раньше большое значение переполняло int и давало отрицательный LIMIT.
  • В комментариях к HTML-просмотрщику есть ссылки на внутренние документы «ADR § 6» и «API-01».
  • В пересобранном disk.uploader.uf-file.bundle.js появился TODO загружать расширения disk и disk.document по требованию.
  • Новых @deprecated нет. Единственная пометка в диффе пришла из пересобранного promo-boost.bundle.js, а в исходнике метод Checker.shouldShowPromoSessionBoost() был помечен @deprecated и до релиза.

Что делать

  • Обновите disk. На старой версии читатель файла может менять пароль и срок его публичной ссылки, а подпись файла открывает все его версии.
  • До обновления проверьте опции unified_link.enabled и unified_link.allow_type_*. Если какая-то из них не равна Y, предупредите пользователей, что единые ссылки включатся.
  • Найдите и поправьте в local/ обращения к $this->signer и ERROR_NO_AI_MODULE, переопределения deleteTree() и deleteNonRecursive(), позиционные вызовы createHandler() и new ExternalLinkHandler($file).
  • В скриптах очистки Диска проверяйте результат deleteTree() и ошибку DISK_FOLDER_22001.
  • Ссылки на версии файлов подписывайте парой «файл + версия» через UnifiedLinkSignature.
  • HTML-просмотрщик включайте опцией disk_enable_html_viewer, только когда intranet на портале знает маршрут /html/.
  • Если ставите сразу и следующую версию модуля, прочитайте разбор disk 26.1150.0.

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

Символ Статус Чем заменять
Bitrix\Disk\DownloadController::$signer Удалено Bitrix\Disk\DownloadController::$unifiedLinkSignature
Bitrix\Disk\Internals\Engine\ActionFilter\CheckReadPermission::$signer Удалено Bitrix\Disk\Internals\Engine\ActionFilter\CheckReadPermission::$unifiedLinkSignature
Bitrix\Disk\Internal\Service\MarkdownRenderService::ERROR_NO_AI_MODULE Удалено Bitrix\Disk\Internal\Service\MarkdownRenderService::ERROR_RENDERER_UNAVAILABLE
Bitrix\Disk\Internal\Service\UnifiedLink\FileHandler\HtmlRenderableFileHandlerFactory::createHandler($forceExternal) Удалено Bitrix\Disk\Internal\Service\UnifiedLink\FileHandler\HtmlRenderableFileHandlerFactory::createHandler($externalLink)

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

disk 26.1150.0 Безопасность Свежее

Disk 26.1100.100 и 26.1150.0: SDK досок больше не шлёт JWT на любой origin, а секрет по умолчанию убран

Диск приехал в коробку двумя версиями одним днём, 21 сентября 2026 года (по официальному каналу «Битрикс24 changelog»). В 26.1100.100 поменялась одна строка, и заметно это только на порталах с сессиями в режиме `separated`. 26.1150.0 закрывает канал `postMessage` в SDK досок, через который JWT и ссы...

2 мин
voximplant 26.700.0 Ломающее Свежее

Voximplant 26.700.0: оценку качества связи убрали, у notifyAdmins() появился тип

Модуль телефонии voximplant 26.700.0 вышел в коробочный Битрикс24 11 сентября 2026 года, по данным официального канала «Битрикс24 changelog». Из карточки звонка убрали оценку качества связи вместе с её JS-событиями, у `Im::notifyAdmins()` появился тип параметра, а публичные константы `SipStatusInfor...

3 мин
ui 26.687.0 Рутинное Свежее

UI 26.687.0: триал VibePlus в облаке и дизайн чипа TintedBitrixGpt

`Bitrix\UI\Controller\InfoHelper` при активации демо сначала спрашивает у модуля bitrix24, включён ли старт VibePlus, и если да, запускает триал VibePlus вместо обычного демо тарифа. Ветка срабатывает только при подключённом модуле bitrix24 и определённой константе `BX24_HOST_NAME`. В `ui.system.chi...

1 мин
ui 26.675.0 Ломающее Свежее

UI 26.675.0: ui.actionpanel переехал в бандл, а rich_text по-новому обходится с квадратными скобками

Модуль ui 26.675.0 собран 26 августа 2026. Из PHP поменялись `Converter` и `Whitelist` в `Bitrix\UI\Format\BBCode`, и пользовательские поля типа `rich_text` теперь иначе сохраняют и индексируют текст с квадратными скобками. На фронтенде старую панель групповых действий `ui.actionpanel` перевели на б...

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

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

на связи

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

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

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

Войти