Перейти к содержимому
08 июн. 2025 г.·7 мин чтения

Доступ LLM к SQL: где проходит граница безопасных запросов

Доступ LLM к SQL полезен для аналитики на естественном языке, но без границ модель сможет читать лишние данные. Разберем роли, фильтры и проверки.

Доступ LLM к SQL: где проходит граница безопасных запросов

Почему прямой доступ к базе опасен

LLM сама по себе не знает ваши бизнес-границы. Она не понимает, какие поля относятся к персональным данным, какие таблицы нельзя трогать днем, а какие расчеты у финансовой команды считаются служебными. Модель видит схему, связи и текст задачи, а потом ищет самый короткий путь к ответу.

Из-за этого даже обычный запрос может вытащить лишнее. Пользователь просит "собрать отчет по клиентам с просадкой выручки", а модель добавляет email, телефон, историю оплат и внутренний сегмент, потому что так ей проще объяснить результат. Для модели это рабочий ход. Для компании - уже лишняя выдача данных.

Проблема не всегда похожа на атаку. Чаще все начинается с нормального аналитического вопроса, который модель понимает слишком широко. Если роль видит всю базу, то доступ LLM к SQL быстро превращается в доступ ко всему, что можно прочитать через SELECT.

Ошибки тут тоже дорогие. Модель может забыть фильтр по дате, собрать тяжелый JOIN на больших таблицах или отправить запрос без LIMIT. Когда она работает прямо с продовой базой, это уже не абстрактная неточность, а нагрузка на живой сервис, рост задержек и поломанные отчеты у людей, которые в этот момент работают в системе.

Отдельная боль - разбор инцидента. Пользователь написал запрос на обычном языке, модель собрала SQL, приложение его выполнило, а потом команда пытается понять, кто решил читать лишние поля и на каком шаге это произошло. Чем шире права, тем больше таких серых зон.

Аудит помогает, но сам по себе не спасает. Сначала нужно урезать доступ до безопасного набора представлений и операций. И только потом давать модели инструменты для аналитики. Даже подробный журнал не исправит слишком широкую роль в базе.

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

Какие задачи стоит отдать модели

Безопасный доступ LLM к SQL начинается с узкой роли. Модель не должна работать с базой как аналитик с полными правами. Ей лучше отдать то, что отнимает время у людей, но не меняет данные.

Хороший пример - перевод вопроса с обычного языка в структуру базы. Если человек пишет: "покажи продажи по регионам за март", модель может сопоставить запрос со схемой и предложить нужные таблицы, поля и связи. Это особенно полезно, когда названия в базе неочевидны, а схема большая.

Следующий безопасный слой - черновик SQL только для чтения. LLM для аналитики нормально собирает SELECT, если вы заранее описали допустимые таблицы, связи и поля. Она может предложить JOIN, добавить фильтр по дате, исключить отмененные заказы и отсортировать результат по выручке.

Обычно модели стоит поручать четыре вещи: искать подходящие таблицы по описанию вопроса, собирать SELECT без команд записи и изменения схемы, работать только с разрешенными агрегациями и фильтрами, а после выполнения запроса объяснять результат простыми словами. Этого уже хватает для многих рабочих сценариев.

На практике это выглядит так: аналитик спрашивает про средний чек по сегментам, модель предлагает запрос, человек быстро смотрит на поля и условия, а потом запускает его в безопасном контуре. Ошибки все еще возможны, но риск уже заметно ниже, чем при свободной генерации любого SQL.

Полезна и обратная задача. После выполнения запроса модель может перевести таблицу в короткий текст вроде: "в марте сильнее всего вырос северный регион, а просадка пришлась на B2B-сегмент". Для бизнеса это часто важнее самого SQL.

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

Где провести границу доступа

LLM не должна видеть базу так же, как аналитик или backend-сервис. Ей нужен узкий коридор: только те данные и те команды, которые нужны для чтения отчетов.

Частая ошибка простая: команде кажется, что пользователь только на чтение уже решает проблему. Это лишь половина защиты. Если модель видит всю схему, она все равно может читать лишнее, строить тяжелые запросы и заходить в таблицы, которые не нужны для аналитики.

Как выглядит нормальная граница

Сначала разделите роли чтения и записи на уровне БД. У модели не должно быть прав, которые есть у приложения, ETL-процессов или админов. Нужны отдельный пользователь, отдельная роль и отдельный набор разрешений.

Потом вынесите для нее отдельную схему только для чтения. В ней лучше держать представления или подготовленные таблицы для отчетов, а не весь продакшен как есть. Если модели нужен отчет по продажам, ей обычно хватает sales_daily, stores и products. Таблицы с пользователями, платежами, внутренними статусами и служебными логами ей не нужны.

Поля с ПДн лучше убрать заранее. Не надейтесь, что модель сама поймет, что паспорт, телефон или email трогать нельзя. Такие колонки безопаснее вообще не показывать. То же касается служебных таблиц, миграций, очередей, таблиц прав доступа и всего, что раскрывает внутреннее устройство системы.

Что стоит запретить жестко

Базовый набор ограничений обычно небольшой. Разрешайте только SELECT по ограниченному списку таблиц или view, блокируйте DDL, UPDATE, DELETE, INSERT, MERGE и TRUNCATE, закрывайте опасные функции, которые читают файлы, ходят в сеть или запускают код. Добавьте лимит на число строк, таймаут и rate limit, чтобы модель не засыпала базу серией похожих запросов.

Эти ограничения защищают не только от утечки, но и от случайного вреда. Даже без злого умысла модель может собрать запрос с тяжелым JOIN на десятки миллионов строк и положить реплику, на которой сидят BI-отчеты.

Хорошая практика - пускать SQL не прямо в продакшен, а через промежуточный слой с понятными правилами. Он проверяет тип запроса, убирает лишние столбцы, добавляет LIMIT, считает частоту вызовов и пишет аудит. Для команд, которым важно хранить логи и ПДн внутри России, такой слой еще и упрощает контроль: видно, кто, когда и зачем запрашивал данные.

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

Как собрать безопасный контур шаг за шагом

Безопасные SQL-запросы не появляются из хорошего промпта. Их дает контур, где модель видит мало, может еще меньше, а каждый запрос проходит проверку до базы.

Обычно схема держится на двух вещах: ограничение прав в базе и отдельный слой проверки. Если LLM ошибется, база все равно не примет опасный запрос.

Сначала сузьте набор данных. Модели редко нужны все схемы и таблицы. Для аналитики ей чаще хватает нескольких витрин с понятными именами и чистой структурой. Если в исходных таблицах есть email, телефон, комментарии операторов или другие чувствительные поля, не показывайте их модели вообще.

Дальше соберите контур по шагам:

  1. Опишите белый список таблиц и полей. Дайте модели только названия разрешенных таблиц, колонок и типов данных.
  2. Создайте отдельную роль только на чтение. Оставьте ей SELECT в нужных витринах и закройте любые изменения, системные таблицы и опасные функции.
  3. Поставьте между моделью и базой слой проверки SQL. Он должен разбирать запрос и отклонять все, что выходит за правила: чужие таблицы, запрещенные поля, запросы без LIMIT, слишком тяжелые JOIN, попытки вызвать функции записи.
  4. Запускайте запросы не в продакшене, а в реплике, витрине или отдельной аналитической копии. Так ошибка не заденет боевую нагрузку.
  5. Логируйте всю цепочку: исходный промпт, SQL, решение проверки, пользователя, время выполнения и объем результата.

Этого уже достаточно, чтобы доступ LLM к SQL стал управляемым, а не опасным экспериментом. Разрешайте модели готовые витрины вроде sales_daily или orders_by_region, а сырые таблицы users, payments_raw и audit_log держите вне ее поля зрения.

Слой проверки лучше строить на явных правилах, а не на новой просьбе к самой модели "оценить безопасность". SQL-парсер работает скучнее, зато надежнее. Он может сразу отклонить запрос, если видит SELECT *, обращение к запрещенной схеме или попытку вытащить слишком много строк.

Логи нужны не для галочки. Аудит запросов помогает быстро понять, где ошиблась модель, где пользователь сформулировал задачу слишком широко и какой шаблон стоит добавить в блок-лист. Если вызовы модели идут через RU LLM, удобно связать аудит модели с журналом SQL и разбирать цепочку целиком.

Что проверять до выполнения SQL

Соберите российский LLM-контур
Подключите модели через один API, если вам нужны 152-ФЗ, аудит и рублевый B2B-инвойсинг.

Перед запуском SQL нужен отдельный фильтр между моделью и базой. LLM легко пишет правдоподобный запрос, но один лишний JOIN или поле с персональными данными уже превращают аналитику в инцидент.

Первое правило простое: пропускайте только чтение. Проверяйте не первое слово, а весь текст запроса. Опасные конструкции часто прячутся внутри CTE, подзапросов и служебных команд. Если парсер видит INSERT, UPDATE, DELETE, ALTER, DROP, TRUNCATE, COPY, CALL или попытку менять сессию, запрос лучше сразу отклонить.

Проверка структуры

Даже обычный SELECT не всегда безопасен. Модель может подтянуть таблицу, к которой ей вообще не стоило обращаться. Поэтому список разрешенных таблиц и представлений лучше держать явно, а все JOIN сверять с этим белым списком. На практике надежнее давать модели доступ не к сырым таблицам, а к подготовленным view для отчетов.

Отдельно смотрите на поля в SELECT. Если там есть email, телефон, паспортные данные, точный адрес, номер договора или другие сырые идентификаторы, фильтр должен остановить запрос или заменить поля на маскированные версии. Для вопроса вроде "покажи продажи по регионам" такие колонки просто не нужны.

Лимит строк и таймаут почти всегда обязательны. Даже без злого умысла модель может сгенерировать тяжелый запрос на миллионы строк. Жесткий LIMIT, ограничение по времени и, если нужно, запрет на полные сканы заметно снижают риск. Для большинства аналитических сценариев хватает первых сотен или тысяч строк.

Проверка смысла

Полезно сравнивать вопрос пользователя с текстом SQL до выполнения. Если человек спросил выручку по магазинам за март, а запрос полез в таблицы клиентов или сотрудников, это явный повод остановиться. Такая проверка ловит много ошибок, которые не видны на уровне синтаксиса.

Для аудита достаточно простого минимума: сохраняйте вопрос пользователя, сгенерированный SQL, итог проверки и причину отказа. Если вы уже используете шлюз с аудит-трейлами и маскированием PII, этот слой удобно держать рядом с проверками SQL. Тогда команда видит не только сам запрос, но и почему система его пропустила или заблокировала.

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

Пример: отчеты по продажам без риска для продакшена

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

В этой витрине нет ФИО, телефонов, адресов, номеров заказов и других лишних полей. Там лежат только обезличенные данные, нужные для отчета. Если вы даете доступ LLM к SQL именно в таком виде, риск резко снижается.

Дальше все просто. Модель получает вопрос, строит SQL только к разрешенной схеме и просит данные у промежуточного сервиса, а не у базы напрямую. Этот сервис проверяет запрос до выполнения.

Он останавливает SQL, если видит обращение не к витрине, а к сырым таблицам, попытку выбрать поля вне белого списка, JOIN с таблицами клиентов или сотрудников, слишком широкий диапазон дат или слишком большой объем строк.

Если менеджер спросил обычный отчет, сервис пропустит запрос и вернет аккуратную сводку: выручка по регионам, изменение к прошлому месяцу, доля региона в общей сумме. Уже по этим цифрам модель делает понятный вывод человеческим языком.

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

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

Хороший признак здорового контура простой: менеджер быстро получает ответ по продажам, а спорные случаи не пролетают на авось. Команда видит в журнале, кто задал вопрос, какой SQL сгенерировала модель, что отрезал фильтр и почему запрос ушел на проверку.

Где команды обычно ошибаются

Соберите безопасный SQL-контур
Подключите RU LLM и держите аудит, логи и маскирование PII внутри РФ.

Самая частая ошибка скучная: команде нужно быстро показать демо, и модель подключают прямо к боевой БД. Пока вопросы простые, все выглядит безопасно. Потом пользователь просит сравнить продажи по регионам за два года, LLM собирает тяжелый JOIN по большим таблицам, и продакшен получает лишнюю нагрузку в рабочий день.

Еще одна типичная путаница - смешивать промпт и права доступа. Фраза в системном сообщении вроде "не делай UPDATE, DELETE и DROP" ничего не гарантирует. Если сервисный пользователь уже имеет эти права, текст не поможет. Модель не должна понимать границы. Границы задает база.

Из-за этого появляется ложное чувство контроля. Команда пишет строгий системный промпт, видит несколько аккуратных SELECT и решает, что контур собран. Но промпт объясняет задачу, а не заменяет роль только на чтение, allowlist таблиц, представления с урезанными полями и проверку SQL перед запуском.

Обычно проблема выглядит так:

  • один общий логин к базе для всех сценариев;
  • доступ к сырым таблицам вместо подготовленных view;
  • фильтр только по словам DELETE, DROP и TRUNCATE;
  • отсутствие лимитов по времени и числу строк.

Часто команды плохо тестируют защиту. Они проверяют только нормальные вопросы и почти никогда не гоняют обходы через UNION, подзапросы, комментарии, смену регистра или неожиданные алиасы. Такой фильтр ломается быстро. Если защита не выдерживает пару десятков злых тест-кейсов, в проде она тоже не выдержит.

С логами история похожая. Многие включают полный аудит запросов, но сохраняют промпты, SQL и ответы как есть. Через неделю в журналах уже лежат телефоны, email, номера договоров и другие ПДн. Для российского контура это плохая привычка: маскирование нужно включать сразу, а не после первого инцидента.

Поэтому зрелая команда разделяет слои защиты. Модель формулирует запрос. Прокси или валидатор проверяет его форму. База видит только урезанные права. Логи хранятся с маскированием и понятным аудитом. Для компаний, которым важно держать этот путь внутри РФ, удобно, когда LLM-шлюз уже поддерживает такие требования.

Быстрая проверка перед запуском

Разведите роли и LLM
Вынесите вызовы модели в отдельный шлюз и не смешивайте их с правами приложения.

Перед первым запуском полезно пройти короткий чек-лист. Он занимает несколько минут, но часто спасает и продакшен, и команду, когда доступ LLM к SQL уже почти готов и очень хочется попробовать все на живых данных.

  • У модели есть отдельная роль только для чтения. Не давайте ей права приложения, BI-сервиса или администратора.
  • Вы заранее собрали белый список таблиц и полей. Модель должна видеть не всю схему, а только данные для конкретных отчетов.
  • Лимит строк, таймаут и отмена долгих запросов уже включены.
  • Защитный слой режет опасные команды до базы и не пропускает выход за рамки белого списка.
  • Для ПДн вы проверили маскирование, журнал доступа и требования 152-ФЗ.

Этого минимума хватает, чтобы отделить безопасные SQL-запросы от опасных экспериментов. Если хотя бы один пункт еще не закрыт, пилот лучше не запускать, даже если задача кажется безобидной.

Тест здесь тоже простой. Попросите модель построить отчет по продажам за месяц. Если она обращается только к разрешенному представлению, получает, например, 100 строк, укладывается в таймаут и не тянет персональные данные, контур собран нормально. Если запрос лезет в сырые таблицы CRM или пытается сделать JOIN с лишними полями, защита должна остановить его автоматически.

Отдельно проверьте аудит запросов. Команда должна видеть, кто отправил запрос, какой SQL сгенерировала модель, что именно ушло в базу и почему система отклонила спорный вариант.

Что делать дальше

Если вы внедряете доступ LLM к SQL, не открывайте модели всю базу сразу. Начните с одной аналитической витрины, где данные уже очищены и понятны по смыслу. Потом дайте модели пару простых вопросов, которые команда задает каждый день, например про выручку по регионам и возвраты по категориям.

Такой старт обычно полезнее большого пилота. На узком наборе таблиц вы быстро поймете, пишет ли модель полезные и безопасные SQL-запросы, путает ли поля и пытается ли выйти за рамки разрешенного контура.

После этого прогоните не красивые демо-вопросы, а реальные промпты сотрудников. Возьмите запросы аналитиков, менеджеров и поддержки в том виде, как они звучат в работе. Протестируйте их на данных для разработки и сравните результат с ожидаемым ответом. Проблемы обычно видны сразу: модель забывает фильтр по дате, делает слишком широкий JOIN или тянет лишние поля.

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

Без журнала вы быстро потеряете контроль. Когда через неделю кто-то спросит, почему отчет посчитал продажи неверно, нужен не общий ответ, а конкретная цепочка: какой был промпт, какой SQL сгенерировала модель и что вернула база.

Если у вас несколько команд и российские требования к данным, такой слой удобно выносить отдельно от приложения. Например, RU LLM на rullm.com работает как OpenAI-совместимый API-шлюз: можно сменить base_url, сохранить существующие SDK и промпты, а логи, маскирование PII и аудит держать внутри РФ. Это не отменяет ограничение прав в базе, но упрощает сборку управляемого контура вокруг LLM.

Хорошая цель на первый месяц проста: одна витрина, 20-30 живых промптов, полный журнал и ручной разбор ошибок раз в неделю. Этого достаточно, чтобы понять, где LLM для аналитики правда экономит время, а где модели пока рано доверять SQL.

Часто задаваемые вопросы

Можно ли дать LLM прямой доступ к продовой базе?

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

Безопаснее пускать ее только к витрине или реплике через слой проверки SQL. Тогда даже при ошибке запрос не заденет боевой сервис.

Какие задачи по SQL стоит доверить модели?

Лучше всего она справляется с чтением и объяснением. Дайте ей поиск нужных таблиц, черновик SELECT, простые агрегации, фильтры и короткое текстовое пояснение по результату.

Запись, изменение схемы и доступ к сырым служебным таблицам ей не нужны. Эти вещи сразу убирают из контура.

Нужна ли для LLM отдельная роль в базе?

Да, нужна. Создайте для модели отдельного пользователя и отдельную роль только на чтение. Не давайте ей права приложения, ETL или администратора.

Так вы зададите границы на уровне базы, а не на уровне надежды, что модель сама все поймет правильно.

Почему одного системного промпта недостаточно для защиты?

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

Границы задают GRANT, белый список таблиц, запрет опасных функций и проверка SQL до запуска.

Где лучше выполнять запросы модели: в проде, реплике или витрине?

Безопаснее запускать запросы в реплике, аналитической копии или витрине. Там вы заранее убираете лишние поля, снижаете риск для боевой нагрузки и проще держите правила доступа.

Продовая база годится только для строго ограниченных сервисов, а не для свободной генерации SQL по текстовым вопросам.

Что нужно проверять перед запуском SQL от LLM?

Смотрите на весь запрос, а не только на первое слово. Фильтр должен пропускать только чтение, сверять таблицы и поля с белым списком, добавлять LIMIT, следить за таймаутом и останавливать слишком тяжелые JOIN.

Полезно еще сравнивать вопрос пользователя с текстом SQL. Если человек спросил выручку по регионам, а запрос полез в клиентов или сотрудников, его лучше не выполнять.

Как защитить персональные данные, если модель работает с SQL?

Самый надежный путь — не показывать модели такие поля вообще. Уберите email, телефон, паспортные данные, точные адреса, номера договоров и сырые идентификаторы из схемы, которую она видит.

Если логи и ПДн должны оставаться внутри РФ, держите аудит, маскирование и хранение журналов в российском контуре с самого начала, а не после первого сбоя.

Нужен ли человек перед выполнением каждого запроса?

Не всегда. Для простых отчетов хватит автоматической проверки правил: разрешенная витрина, только SELECT, лимит строк и понятный диапазон дат.

Когда запрос просит детализацию по клиентам, спорные поля или необычно широкий срез, отправляйте его на ручное подтверждение. Так команда не тормозит рутину, но держит риск под контролем.

Как понять, что безопасный контур уже работает?

Проверьте несколько вещей на живых сценариях. Модель должна обращаться только к разрешенным view, укладываться в таймаут, возвращать ограниченный объем строк и не тянуть ПДн.

Еще посмотрите журнал целиком: вопрос пользователя, сгенерированный SQL, решение валидатора и итог базы. Если по этой цепочке легко разобрать любой сбой, контур собран нормально.

С чего начать внедрение LLM для аналитики по SQL?

Начните с одной витрины и пары частых вопросов, например по выручке и возвратам. Дайте модели только SELECT, прогоните 20–30 реальных промптов сотрудников и сравните ответы с тем, что ожидает аналитик.

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