Приватные поля ORM: select и filter ведут себя по-разному
Проблема/контекст
В 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.
Комментарии (0)
Пожалуйста, войдите в аккаунт, чтобы оставить комментарий
Оставить комментарийПока нет ни одного комментария. Будьте первым!