Приватные поля ORM: select и filter ведут себя по-разному

27.05.2026

Проблема/контекст

В D7 ORM поле можно пометить как приватное через 'private' => true или configurePrivate(). Такие поля скрыты от обычных запросов: ядро считает их служебными или чувствительными. Классический пример — PASSWORD в Bitrix\Main\UserTable, где хеш пароля не должен попадать в типовые выборки списков и отчётов.

Разработчик часто ожидает, что Query::enablePrivateFields() снимает все ограничения. Это не так: перед сборкой SQL ядро вызывает checkForPrivateFields(), и private-поля всегда запрещены в filter. В select и runtime они доступны только после явного разрешения. Тот же запрет распространяется на ExpressionField, если в его buildFrom участвует private-колонка — метод Query::isFieldPrivate() обходит цепочки выражения.

Решение с кодом

Попытка выбрать PASSWORD без флага завершится SystemException с текстом про enablePrivateFields(). Фильтрация по private-полю упадёт в любом случае — даже после enablePrivateFields().

        <?php
declare(strict_types=1);

use Bitrix\Main\UserTable;

// Ошибка: Private field ... PASSWORD is restricted in query
UserTable::query()
    ->setSelect(['ID', 'PASSWORD'])
    ->where('ACTIVE', 'Y')
    ->fetchAll();

    

Для чтения private-полей передайте 'private_fields' => true в getList() или вызовите enablePrivateFields() у query. Это удобно в админских скриптах и сервисах миграции, где доступ к служебным колонкам действительно нужен.

        <?php
declare(strict_types=1);

use Bitrix\Main\UserTable;

$rows = UserTable::getList([
    'select' => ['ID', 'LOGIN', 'PASSWORD'],
    'filter' => ['=ACTIVE' => 'Y'],
    'private_fields' => true,
    'limit' => 10,
])->fetchAll();

    

Фильтр по private-полю ядро не пропустит — в checkForPrivateFields() проверка filter_chains выполняется до проверки флага private_fields_on:

        <?php
declare(strict_types=1);

use Bitrix\Main\UserTable;

// Ошибка: Private field ... PASSWORD is restricted in filter
UserTable::query()
    ->enablePrivateFields()
    ->setSelect(['ID'])
    ->where('PASSWORD', 'some_hash')
    ->fetchAll();

    

В своих DataManager помечайте служебные колонки явно и не используйте их в публичных API:

        (new StringField('INTERNAL_TOKEN'))
    ->configurePrivate(),

    

Если нужна выборка «по секретному значению», добавьте отдельный публичный метод сервиса с параметризованным SQL или whitelist-фильтром по не-private полю. Не расширяйте filter ORM и не снимайте configurePrivate() ради одного запроса — это откроет поле для всех последующих вызовов getList() без контроля.

Итог

configurePrivate() защищает поле от случайного попадания в запросы и API. enablePrivateFields() и 'private_fields' => true разрешают только select и runtime; filter для private-полей в ORM закрыт намеренно и без обходного пути в query builder. Для поиска по чувствительным данным проектируйте отдельный контур, а не пытайесь «обойти» ограничение через WHERE.

Опубликовано 14 часов назад

Комментарии (0)

Пожалуйста, войдите в аккаунт, чтобы оставить комментарий

Оставить комментарий

Пока нет ни одного комментария. Будьте первым!

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