В этом руководстве подробно описан метод, который действительно работает: структурное ограничение на SELECT, закрытый белый список функций, обязательное ограничение строк и блокировка запрашиваемой схемы. Каждый шаг опирается на валидатор, фактически реализованный в механизме NL2SQL Aurabase, возможности его собственного искусственного интеллекта , встроенного в бэкэнд, а не стороннего сервиса, созданного постфактум. Если эта тема для вас нова, наш обзор NL2SQL закладывает основы, а пошаговое руководство показывает, как создать полную конечную точку.
Самое необходимое
- Оперативное проектирование («генерирует только SELECT») — это , а не проверка безопасности: модель может галлюцинировать, руководствоваться неоднозначным вопросом или просто игнорировать инструкции.
- Соблюдаемая проверка является структурной: синтаксический анализатор создает синтаксическое дерево (AST) запроса и по умолчанию отклоняет все, что не разрешено явно.
- Четыре конкретных уровня ограничивают риск: строгий SELECT (ни подзапрос, ни CTE, ни UNION), закрытый белый список из десяти функций,
LIMITобязательный и ограниченный, заблокированный доступ к системному каталогу и нетенантным схемам. - Запрашиваемая схема должна исходить от сервера , а не из поля клиентского запроса: в противном случае ничто не мешает вызывающему объекту предоставить свою собственную схему для обхода проверки.
- В Aurabase этот валидатор (крейт Rust
sqlparser) тестируется на состязательных случаях, описанных в коде: запрещенные функции, скрытые вFILTER, во внутриагрегатеORDER BYили вOFFSET.
Почему инструкция в системной подсказке ничего не блокирует?
Системное приглашение с надписью «генерирует только запросы SELECT» является предпочтением, а не препятствием. Модель уважает это большую часть времени, потому что она обучена следовать инструкциям, а не потому, что технические ограничения физически не позволяют ей писать что-либо еще. Два класса неудач делают эту уверенность недостаточной в производстве.
Первое вытекает из самого вопроса. Пользователь, злонамеренный или просто креативный в своей формулировке, может направить вопрос так, чтобы подтолкнуть модель к SQL, которую ему не следовало писать: объединение с чувствительной таблицей, фильтр, обходящий ожидаемую логику, вызов системной функции. Модель не делает различия между законным вопросом и вопросом, предназначенным для манипулирования им.
Второе не требует злого умысла. Модель может галлюцинировать имя таблицы, забыть LIMIT, запрошенный в подсказке, или сгенерировать SELECT * без каких-либо ограничений для большой таблицы. Результат в обоих случаях один и тот же: потенциально дорогостоящий или навязчивый SQL, который прошел фильтр подсказок и вот-вот будет выполнен на реальной базе данных.
Системная подсказка остается полезной, большую часть времени она направляет модель к правильному результату. Но знак «проход запрещен» не останавливает того, кто не умеет читать или решает его игнорировать. Вам нужна закрытая дверь сзади, а не просто панель спереди.
Анализируйте сгенерированный SQL в синтаксическое дерево, а не в необработанную строку.
Первая линия защиты — проанализировать SQL, созданный моделью, с помощью реального синтаксического анализатора целевого диалекта, а затем проверить полученную структуру, а не необработанный текст. Поиск запрещенных слов в строке символов («DROP», «DELETE», «;») обходится тривиально: разный регистр, комментарий, вставленный в середину ключевого слова, набранные кавычки. Синтаксическое дерево однозначно описывает, что на самом деле делает запрос.
Aurabase реализует этот шаг с помощью крейта Rust sqlparser и его диалекта PostgreSqlDialect. Еще до синтаксического анализа первый лексический фильтр отклоняет две конструкции, которые трудно правильно обосновать в дереве: долларовые кавычки ($$...$$), которые могут скрыть произвольное содержимое строки, и многострочные комментарии (/* */), которые могут скрыть реальный конец инструкции.
Этот отказ от использования нескольких операторов блокирует наиболее известную форму SQL-инъекции путем стека: SELECT * FROM users; DROP TABLE users;--. Парсер возвращает только одну действенную инструкцию, вторая просто никогда не достигается, независимо от того, как она сформулирована в исходном вопросе.
Структурно ограничиться простым SELECT.
После получения дерева самая широкая проверка состоит в том, чтобы принять только один тип корневого узла, запрос (Statement::Query), и отклонить все остальное: INSERT, UPDATE, DELETE, DROP, CREATE, ALTER. Это уже не подсказка, а условие на тип разбираемого объекта, обойти которое не может никакая умелая постановка вопроса.
Даже внутри SELECT некоторые конструкции остаются опасными и заслуживают явного отказа:
| Строительство отклонено | Почему это опасно? |
|---|---|
| КТР/С | Может связать дополнительную непреднамеренную логику перед окончательным SELECT. |
| Подзапросы, UNION/INTERSECT/EXCEPT | Расширяет область действия одного вопроса в одном запросе. |
| ВЫБРАТЬ... В | Создает таблицу: письмо замаскировано под чтение. |
| ДЛЯ ОБНОВЛЕНИЯ / ДЛЯ ПОДЕЛИТЬСЯ | Установка замков, риск возникновения конфликтов с производственным трафиком. |
| Табличные функции (generate_series, pg_read_file...) | Доступ к системе или отказ в обслуживании через линии, генерируемые по требованию. |
Тестовый пример, взятый из репозитория, конкретно иллюстрирует последний пункт: SELECT * INTO backup FROM users отклонен, даже если он не содержит ни ключевого слова видимой записи, ни подозрительной функции. Формы запроса достаточно, чтобы его дисквалифицировать.
Белый список функций, а не черный список
Черный список запрещенных функций (pg_sleep, pg_read_file, dblink...) требует предвидеть каждую опасную функцию одну за другой, в то время как Postgres раскрывает несколько сотен из них. Белый список меняет бремя доказательства: разрешено только десять функций: count, sum, avg, min, max, lower, upper, coalesce, date_trunc, now. Все остальное запрещено по умолчанию, включая одну законную функцию, которую еще никто не догадался добавить.
Одного прохода проверки не всегда достаточно. Структурный обход дерева перечисляет его точки входа одну за другой (проекция, WHERE, JOIN, GROUP BY...), и одну легко забыть: запрещенную функцию можно спрятать в предложении FILTER (WHERE pg_sleep(10) IS NOT NULL), во внутриагрегатном ORDER BY (sum(id ORDER BY pg_sleep(10))), в WITHIN GROUP, DISTINCT ONили OFFSET.
Поэтому валидатор Aurabase добавляет второй исчерпывающий проход, который проходит через все выражения в дереве, где бы они ни находились, независимо от структурного пути. Это предполагаемая глубокая защита: если первый проход пропускает случай, второй его догоняет.
Привязка возвращаемых строк: LIMIT обязателен и ограничен.
SELECT * остается авторизованным, это полезно для интеллектуального анализа данных. Риск не в главной роли, а в отсутствии ограничения на запрос, написанный моделью: плохо сформулированный вопрос может вернуть целую таблицу, что влечет за собой затраты памяти и время ответа.
Aurabase применяет простое и прозрачное правило. Если сгенерированный SQL не имеет LIMIT, сервер добавляет его (по умолчанию 100 строк, значение объявляется модели в системном приглашении). Если SQL запрашивает LIMIT сверх жесткого ограничения (по умолчанию 1000 строк), запрос явно отклоняется, а не сокращается автоматически. Оба значения настраиваются на стороне сервера (AI_NL2SQL_DEFAULT_LIMIT, AI_NL2SQL_MAX_LIMIT), и сервер даже отказывается запускаться, если ошибка превышает потолок.
Отказ, а не молчаливое сокращение имеет прямой интерес: ограничение, установленное без предупреждения, создаст у звонящего иллюзию, что его запрос был выполнен, в то время как результат был бы усечен без его ведома. limit_injected всегда указывает, исходит ли значение от модели или от сервера.
Блокировка доступа к схеме: системный каталог и кросс-схема
Две отдельные утечки угрожают механизму NL2SQL, подключенному к реальной базе данных: доступ к системному каталогу Postgres и доступ к схеме, которая не принадлежит вызывающей стороне. Оба блокируются после проверки, независимо от какой-либо политики RLS, размещенной в нисходящем направлении.
pg_catalog всегда является частью search_path, что означает, что неквалифицированное имя, такое как pg_authid или pg_stat_activity, обращается к нему напрямую, без префикса. Валидатор Aurabase блокирует любое имя, начинающееся с pg_, а также information_schema и внутреннюю схему aura_console, независимо от того, квалифицировано оно или нет.
Для двухкомпонентного имени (schema.table) разрешена только схема вызывающего проекта, любое другое значение отклоняется. Имя, состоящее из трех и более компонентов, автоматически отклоняется. Эта граница на уровне сгенерированного запроса является дополнением к изоляции уровня базы данных, подробно описанной в нашей статье о многопользовательской изоляции: одна не позволяет сгенерированному SQL нацеливаться на другую схему, другая предотвращает попадание самого соединения в другую базу данных. Ни одно не заменяет другое.
Никогда не позволяйте клиенту переопределять запрашиваемую схему.
Дискретная ловушка подстерегает любой NL2SQL API, который принимает параметр, описывающий схему или таблицы, разрешенные в запросе клиента. Если этот же параметр используется для создания приглашения и проверки выходного SQL, вызывающая сторона может лгать о том, что разрешено, и затем проверка проверяется на соответствие этой лжи, а не реальности базы данных.
Aurabase анализирует фактическую базовую схему проекта при каждом вызове с коротким тридцатисекундным кешем для обеспечения производительности и явно отклоняет (ошибка 400) любые поля schema, allowed_schemaили schema_context , отправленные в теле запроса, вместо того, чтобы принимать и затем молча перезаписывать его. Разница имеет значение: поле, принятое, а затем проигнорированное, создает иллюзию контроля, которого не существует; отказное поле говорит об этом сразу.
Аудит вашего собственного конвейера NL2SQL перед производством
Независимо от того, используете ли вы Aurabase или создаете свой собственный конвейер на основе обычного LLM, следующие пункты охватывают то, что чаще всего упускают из виду.
Если вы сами пишете валидатор
- Разбирайте SQL с помощью реального синтаксического анализатора для вашего конкретного диалекта, а не с помощью сопоставления с образцом в строке.
- Примите отказ по умолчанию: любой тип узла, любая функция, не разрешенная явно, должны быть отклонены, а не только уже выявленные опасные случаи.
- Принимайте только один оператор для каждого запроса. Это самый простой отказ от составных запросов.
- Тестируйте валидатор на реальных состязательных случаях (функция запрещена в FILTER, во внутриагрегатном ORDER BY, в OFFSET), а не только на очевидных случаях.
- Несмотря ни на что, выполняйте проверенный SQL с ролью Postgres с пониженными привилегиями в отношении ожидаемой схемы: валидатор ограничивает форму запроса, роль ограничивает то, чего он может физически достичь, если случай ускользнул от вас.
Если вы оцениваете стороннюю платформу NL2SQL
- Спросите прямо, является ли проверка структурной (AST) или это всего лишь подсказка: ответ меняет все.
- Убедитесь, что ограничение строки применяется по умолчанию, а не просто задокументировано как лучшая практика за ваш счет.
- Проверьте, может ли схема, используемая для проверки, быть предоставлена клиентом API, который снова откроет именно описанную выше ошибку.
- Прежде чем сделать выбор, сравните несколько инструментов по этому конкретному критерию: в нашем сравнении инструментов NL2SQL подробно рассказывается, что отличает подходы, доступные в 2026 году.
Валидатор снижает риск, не заменяет RLS
Надежный валидатор AST снижает риск в источнике: SQL, попадающий в вашу базу данных, уже имеет известную и ограниченную форму. Однако он не заменяет политики RLS в ваших конфиденциальных таблицах, которые определяют, какие строки имеет право видеть данный пользователь. Два уровня отвечают на разные вопросы: валидатор ограничивает форму сгенерированного запроса, RLS ограничивает данные, которые он может вернуть для конкретного пользователя. Сохраняйте оба активными, даже если один кажется избыточным по отношению к другому.
NL2SQL отвечает на структурированные вопросы о ваших таблицах. Для вопросов о неструктурированном контенте, документах, заметках, билетах собственный RAG Aurabase использует аналогичную логику безопасности, подробно описанную в нашем руководстве по конвейеру RAG на pgvector.