Перейти к содержимому

MCP

Аудит Claude Code и MCP-серверов в локально развёрнутых средах

Автор Olivares AI 6 мин чтения

Платформенная команда разворачивает Claude Code для десятка инженеров. Чтобы инструмент приносил пользу, его подключают к нескольким MCP-серверам: один читает монорепозиторий, другой обращается к реплике для отчётности, третий заводит тикеты. В течение недели агент уже читает исходный код, обращается к базе данных и вызывает внутренние инструменты — и никто не может ответить на вопрос, который рано или поздно задаст аудитор: какой агент что сделал, с каким ресурсом и можете ли вы доказать, что данные не были изменены задним числом?

Это не гипотетический пробел. Независимое отраслевое исследование (CSA/Token, n=418) показало, что примерно у 82% организаций работают AI-агенты, о которых они не знают, и лишь около 21% ведут их инвентаризацию в реальном времени. Claude Code и MCP — ровно тот класс возможностей, который внедряется быстро и опережает аудиторскую готовность. Хорошая новость: защищённый журнал достижим целиком внутри вашего периметра, без выхода за его пределы каких-либо полезных данных или секретов.

Почему очевидная конфигурация не проходит аудит

При типичном первом развёртывании используется один общий набор учётных данных. Каждый экземпляр Claude Code аутентифицируется на уровне MCP и в нижестоящих базах данных под одной и той же служебной учётной записью — mcp-runner, ci-bot, что-то столь же обезличенное. Это работает и незаметно разрушает привязку действий.

Когда десять агентов действуют как один субъект, журнал аудита базы данных показывает десять операций записи от mcp-runner — и не более того. Вы не можете определить, какая инженерная сессия выполнила UPDATE, был ли он инициирован из интерактивного запроса Claude Code или из фонового задания, какой MCP-инструмент оказался в цепочке вызова. По данным независимого исследования (Optro), доля организаций, способных проследить действие агента до конкретного человека, составляет около 28%. Общая служебная учётная запись — одна из структурных причин, по которым привязка действий рушится: журнал аудита может показать, что произошло, но никогда — какой агент это сделал.

Второй провал — целостность. Даже команды, которые ведут журнал по каждому действию, обычно пишут его в изменяемое хранилище. Если журналы лежат в той же системе, до которой может добраться злоумышленник (или сбойный агент), формулировка «у нас есть журналы» — не то же самое, что «у нас есть доказательства». Аудитор проводит между ними чёткую границу.

Идентичность для каждого агента — это фундамент

Всё, что строится дальше, зависит от привязки действий, поэтому сначала наведите порядок с идентичностью. Вместо одного общего токена выдавайте отдельную короткоживущую идентичность каждому агенту — в идеале каждой сессии. Идентичность сопровождает запрос внутрь MCP-сервера и в любой ресурс, к которому обращается агент, поэтому субъект, зафиксированный на стороне базы данных, — это сам агент, а не обезличенный runner.

Именно это наполняет смыслом принцип least-privilege. Агента для отчётности можно закрепить в режиме только на чтение на реплике для отчётности; релизному агенту даётся запись лишь в те артефакты, которыми он владеет. Теперь единичная зафиксированная операция записи за пределами этих рамок — точный, привязываемый сигнал, а не шум в общей учётной записи. Сформулированное как политика, намерение предельно прямолинейно:

agent "reporting-assistant" {
  # Сессия Claude Code через MCP-сервер отчётности
  resource "prod-postgres/reporting_replica" {
    access     = "read"      # SELECT only
    deny       = ["write", "ddl"]
  }
  resource "s3://billing-exports" {
    access = "read"
  }
  on_violation {
    action = "block_and_alert"   # отказать в момент доступа, не только логировать
  }
}

Дело не в синтаксисе — дело в том, что политика называет агента, называет ресурс и разделяет чтение от чтения/записи. Именно это разделение и есть вся суть.

Относитесь к аннотациям MCP как к недоверенным сигналам

MCP-инструменты могут описывать себя аннотациями вроде readOnlyHint и destructiveHint. Они действительно полезны для первичной сортировки — инструмент, объявляющий себя деструктивным, заслуживает более жёсткой политики. Но спецификация MCP прямо указывает, что это подсказки, и клиенты не должны полагаться на них при принятии решений о безопасности. Они исходят от сервера — а именно его вы и подвергаете аудиту. Инструмент может объявить readOnlyHint: true и всё равно выполнить запись — из-за ошибки, неверной настройки или намеренного обхода.

Поэтому верная позиция — подтверждение, а не доверие. Воспринимайте аннотацию как заявление, а затем сверяйте её с фактической истиной с уровня, который инструмент не контролирует:

  • Журналы аудита базы данных (например, PostgreSQL pgAudit) показывают, действительно ли выполнялся SELECT или UPDATE.
  • Спаны OpenTelemetry от MCP-сервера и нижестоящих сервисов показывают граф вызовов и выполненные операции.
  • Сигналы ядра eBPF — последний рубеж против обхода: системный вызов записи в файл или сокет наблюдаем на уровне ядра независимо от того, что инструмент заявил в пользовательском пространстве.

Когда аннотация говорит «только чтение», а ядро увидело запись, это противоречие — главный вывод: инструмент с readOnlyHint, выполнивший запись в prod-postgres, — это ровно тот дрейф от least-privilege, ради которого стоит кого-то поднять среди ночи. Расхождение между тем, что политика разрешила, и тем, что сборщики наблюдали, — вот где живёт реальный риск.

Журнал, который примет аудитор

Журнал становится доказательством, только если он защищён от подделки. Записывайте каждое событие в журнал, доступный только для добавления, с хеш-цепочкой: каждая запись включает хеш предыдущей, поэтому любая правка или удаление ниже по цепочке разрывает её и становится обнаружимым. Каждая строка привязывает действие к конкретному агенту, называет ресурс, фиксирует чтение или чтение/запись и несёт уровень достоверности — attributed, когда и идентичность, и результат подтверждены, approximate, когда что-то пришлось вывести логически. Достоверность показывается честно; приблизительное совпадение никогда не выдаётся за доказанное.

ts=2026-06-08T09:14:02Z agent=reporting-assistant@s3f1 tool=sql-read     resource=prod-postgres/reporting_replica  op=R   outcome=allow   conf=attributed   prev=8a1c…
ts=2026-06-08T09:14:05Z agent=reporting-assistant@s3f1 tool=export-writer resource=s3://billing-exports          op=R   outcome=allow   conf=attributed   prev=2f90…
ts=2026-06-08T09:17:48Z agent=data-export-job@b22e     tool=sql-write     resource=prod-postgres/customers          op=RW  outcome=DENY    conf=attributed   prev=c7d3…  policy=read_only_violation

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

Ещё два свойства делают журнал устойчивым. Привилегированные просмотры сами проходят аудит: обращение к карте доступа фиксируется, поэтому на вопрос «кто что видел» можно ответить. И экспорт доказательств защищён от подделки — вы передаёте аудитору подписанный фрагмент цепочки, который он может проверить независимо, а не CSV, который ему придётся принимать на веру.

СвойствоОбщая служебная учётная записьИдентичность для каждого агента + журнал с хеш-цепочкой
Привязка действийОдин субъект для всех агентовДействие привязано к конкретному агенту/сессии
Чтение и записьСмешаныРазделены и проверяются политикой
ЦелостностьИзменяемые журналыТолько добавление, цепочка рвётся при правке
Экспорт, готовый для аудитораCSV на доверииПодписанный, независимо проверяемый фрагмент

Почему здесь важна локальная развёртка

Всё это работает без выхода чего-либо за пределы вашей сети. Сборщик наблюдает — журналы, OpenTelemetry, eBPF как рубеж на уровне ядра — а не встраивается в путь данных агента, поэтому при сбое он никогда не ломает ни агента, ни стоящий за ним production-запрос. Журнал хранит отношения доступа, а не полезные данные: он фиксирует, что reporting-assistant прочитал reporting_replica, но не возвращённые им строки. Входные данные, способные нести секреты или PII, редактируются и проходят сканирование на секреты до того, как что-либо будет записано. Для air-gapped-сред, контуров под GDPR или с ограничениями по резидентности данных это разница между аудиторской готовностью, которую можно отстоять, и той, которую отстоять нельзя: никакие данные об использовании Claude Code и MCP не покидают вашу сеть, потому что система изначально не видит сами данные.

Claude Code и MCP стоят того, чтобы их развернуть. Им просто нужен аудиторский журнал, выстроенный так же, как вы выстроили бы его для любой другой привилегированной автоматизации — сначала идентичность, затем подтверждение результатов, затем закрепление доказательств. Если вы хотите увидеть, как карта доступа и защищённый от подделки журнал сочетаются друг с другом, модель безопасности и обзор продукта раскрывают каждый из них подробнее.

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

Можно ли доверять полю readOnlyHint MCP-инструмента как доказательству того, что действие было операцией только на чтение?

Нет. Спецификация MCP прямо указывает, что аннотации инструментов, такие как readOnlyHint и destructiveHint, являются подсказками и должны рассматриваться как недоверенные, поскольку они исходят от сервера, а не от уровня принудительного контроля. Используйте их для первичной сортировки и для выявления противоречий, но фактический результат чтения или записи доказывайте телеметрией или сигналами уровня ядра (спаны OpenTelemetry, журналы аудита базы данных, eBPF). Доверяйте подтверждённому результату, а не аннотации.

Как привязать действие Claude Code к конкретному агенту, а не к общей служебной учётной записи?

Выдавайте отдельную короткоживущую идентичность каждому агенту или каждой сессии вместо одного общего токена. Когда агент обращается к базе данных или к MCP-серверу, эта идентичность сопровождает запрос, поэтому журнал аудита фиксирует, какой именно агент что сделал. Общая служебная учётная запись сводит всех агентов к одному субъекту и делает невозможным как принцип least-privilege на уровне агента, так и привязку действий, устойчивую к подделке, постфактум.

Узнайте, до чего могут добраться ваши агенты

Olivares AI — открытая self-hosted платформа для управления вашим парком AI. Разверните её на собственной инфраструктуре и получите карту доступа, которую давно запрашивают ваши команды безопасности и платформ.