Система принимает произвольный текст от пользователя и на его основе формирует запросы к корпоративному хранилищу данных. Исходное допущение при проектировании: языковая модель считается недоверенным источником. Защита строится так, как если бы модель была полностью скомпрометирована и формировала произвольный текст запроса.
Из этого следует главное правило: попытки подменить инструкции через текст вопроса не фильтруются на входе. Способов переформулировать такую попытку бесконечно много, и фильтрация входа является тупиковым путём. Вместо этого система построена так, что успешная подмена инструкций ничего не даёт: сформированный запрос к закрытой схеме не проходит проверку по перечню разрешённых объектов, а если бы прошёл, то у учётной записи приложения нет прав на эту схему.
Защита разделена на три независимых уровня. Обход одного не даёт обхода остальных.
| Рубеж | Механизм |
|---|---|
| 0 | Ограничение вывода грамматикой языка T-SQL на сервере vLLM. Синтаксически неверный запрос не может быть сформирован |
| Рубеж | Механизм |
|---|---|
| 1 | Разбор запроса в синтаксическое дерево библиотекой sqlglot. Отказ при невозможности разбора |
| 2 | Структурные правила: ровно одна инструкция, только SELECT, отсутствие имён из четырёх частей, отсутствие соединений без условия, отсутствие узлов INTO, EXEC, OPENROWSET |
| 3 | Проверка всех обращений к таблицам по перечню разрешённых витрин. Обход всего дерева, включая вложенные подзапросы и выражения WITH |
| 4 | Принудительное ограничение числа строк через синтаксическое дерево |
| 5 | Оценка стоимости выполнения до запуска запроса через SET SHOWPLAN_XML ON |
| Рубеж | Механизм |
|---|---|
| 6 | Отдельная учётная запись, DENY на базовые таблицы, права чтения только на представления схемы mart, ограничение времени выполнения, отдельный пул ресурсов |
Проверка блокирует запрос, если не может доказать его безопасность. Формулировка «не нашли ничего запрещённого, значит пропускаем» считается ошибочной. Верная формулировка: «не смогли проверить, значит отказ».
Обратное поведение является классической уязвимостью: атакующий формирует запрос, который система проверки не разбирает, а СУБД исполняет.
Проверка по перечню запрещённых слов обходится тривиально:
SELECT * FROM mart.v_sales;/**/DROP TABLE dbo.ordersНо основная проблема не в обходах. Поиск по тексту отвечает только на вопрос «есть ли в запросе запрещённые слова». Нужный вопрос звучит иначе: «к каким объектам базы данных обращается этот запрос». На него отвечает только разбор в синтаксическое дерево.
Текст ошибки СУБД пользователю не показывается. Сообщение вида
Invalid object name 'hr.salaries' подтверждает наличие или отсутствие объекта
в хранилище, а серия таких вопросов раскрывает структуру закрытых схем, права
на которые не выданы.
Пользователю показывается обобщённая формулировка и код обращения. Полный текст ошибки записывается в журнал и передаётся языковой модели для исправления запроса: модель является единственным участником, способным её исправить.
Не покидают. Система работает полностью внутри контура организации:
- языковая модель размещена локально на сервере организации;
- обращения к внешним службам отсутствуют;
- содержимое хранилища во внешние сети не передаётся.
В языковую модель передаётся ограниченное число строк результата, по умолчанию не более ста, и только для формирования текстового вывода. Полный результат запроса в модель не передаётся никогда.
Уязвимости принимаются через закрытое обращение в разделе Security Advisories репозитория. Публичное обсуждение до выпуска исправления не проводится.
Ожидаемый срок первичного ответа: три рабочих дня.
При обращении просьба указать:
- версию системы;
- последовательность действий для воспроизведения;
- какой рубеж защиты был пройден и каким способом;
- предполагаемое влияние.
Скрипт sql/03_security.sql завершается самопроверкой: он выполняет заведомо
запрещённое обращение к базовой таблице от имени учётной записи приложения
и завершается с ошибкой, если это обращение прошло успешно. Развёртывание
с неверно настроенными правами остановится на этом шаге.