Безопасность рантайма

Как ограничить опасные действия AI-агентов через разрешения и карточки

Как ограничить опасные действия AI-агентов через разрешения и карточки
Temporary fallback cover; replace in editorial pass.
Уровень: продвинутый Время чтения: до 12 минут Практика: policy-as-code
Самое важное из мира AI — в канале MAX AgentLabОтдельные разборы, инструменты и практические схемы Читайте Agent Lab в Telegram Разборы, кейсы и новости о практических AI-агентах без лишней воды.

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

Главный принцип. Агенту нельзя выдавать «доступ к среде» вообще. Ему выдают конкретное право на конкретную операцию, с конкретными ограничениями и коротким сроком действия.

Почему технический доступ становится опасным

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

Проверять нужно не только имя инструмента. Вызов должен пройти несколько независимых проверок:

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

Модель карточки разрешений

Карточка — это машиночитаемое описание одного короткоживущего права. Для практической проверки достаточно пяти обязательных полей: tools, scope, limit, expires_at и operations.

{
  "id": "analysis-read-01",
  "subject": "log-analyzer",
  "tools": ["fs.read"],
  "scope": {
    "paths": ["/workspace/logs/*.json"]
  },
  "operations": ["read"],
  "limit": {
    "calls": 20,
    "bytes": 1048576
  },
  "expires_at": "2026-11-06T18:00:00Z",
  "decision": "allow"
}

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

tools
Явный список инструментов. Если инструмент не указан, вызов должен быть запрещён.
scope
Ресурсы, к которым применяется право: пути, идентификаторы записей, домены или пространства имён.
operations
Разрешённые действия внутри инструмента. Чтение не должно подразумевать запись или удаление.
limit
Ограничение количества вызовов, объёма данных, суммы или других измеримых параметров.
expires_at
Момент, после которого карточка недействительна даже при полном совпадении остальных полей.

Шаг 1. Опишите роль агента одним предложением

Начните не со списка доступных инструментов, а с результата задачи. Например: «Агент читает JSON-логи из каталога проекта и формирует сводку без изменения файлов». Это предложение задаёт границу: чтение допустимо, запись и удаление — нет.

Затем выпишите минимальный набор операций. Для указанного примера он будет выглядеть так:

role: log-analyzer
allowed:
  - tool: fs.read
    operation: read
    scope: /workspace/logs/*.json
denied:
  - fs.write
  - fs.delete
  - net.request

Явный список запрещённых операций полезен для ревью и тестов, хотя техническое правило должно строиться по принципу deny by default: всё, что не разрешено, запрещено.

Шаг 2. Зафиксируйте область действия

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

{
  "scope": {
    "paths": [
      "/workspace/logs/2026-11-06/*.json"
    ]
  }
}

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

Шаг 3. Добавьте лимит и срок действия

Лимит отвечает на вопрос «сколько», срок — на вопрос «как долго». Оба ограничения нужны одновременно. Срок не заменяет лимит: действующая карточка всё ещё может разрешить слишком много вызовов. Лимит не заменяет срок: забытая карточка может остаться активной после завершения задачи.

{
  "limit": {
    "calls": 20,
    "bytes": 1048576
  },
  "expires_at": "2026-11-06T18:00:00Z"
}

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

Шаг 4. Проверяйте карточку до вызова инструмента

Проверка должна находиться перед исполнителем инструмента, а не только в системном промпте. Упрощённый алгоритм можно выразить так:

function authorize(request, card, now) {
  if (card.decision !== "allow") return deny("card_not_allowed");
  if (now >= card.expires_at) return deny("card_expired");
  if (!card.tools.includes(request.tool)) return deny("tool_not_allowed");
  if (!card.operations.includes(request.operation)) {
    return deny("operation_not_allowed");
  }
  if (!matchesScope(request.resource, card.scope)) {
    return deny("scope_mismatch");
  }
  if (exceedsLimit(request, card.limit)) {
    return deny("limit_exceeded");
  }
  return allow();
}

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

Шаг 5. Запустите безопасный воспроизводимый тест

Ниже — пример локальной проверки. Она не предполагает доступа к реальной системе: команды создают временный каталог, файл и два JSON-описания. Это демонстрация интерфейса, а не утверждение о конкретном API вашего агента.

WORKDIR="$(mktemp -d)"
mkdir -p "$WORKDIR/logs"
printf '%s\n' '{"level":"info","message":"started"}' > "$WORKDIR/logs/app.json"

cat > "$WORKDIR/allow-card.json" <<'JSON'
{
  "tools": ["fs.read"],
  "scope": {"paths": ["LOG_PATH"]},
  "operations": ["read"],
  "limit": {"calls": 1},
  "expires_at": "2099-01-01T00:00:00Z",
  "decision": "allow"
}
JSON

cat > "$WORKDIR/deny-request.json" <<'JSON'
{
  "tool": "fs.delete",
  "operation": "delete",
  "resource": "LOG_PATH"
}
JSON

sed -i "s|LOG_PATH|$WORKDIR/logs/app.json|g" \
  "$WORKDIR/allow-card.json" "$WORKDIR/deny-request.json"

printf '%s\n' "Карточка: $WORKDIR/allow-card.json"
printf '%s\n' "Опасный запрос: $WORKDIR/deny-request.json"

Теперь передайте запрос fs.delete в тот же слой авторизации, который используется агентом. Ожидаемый результат теста:

{
  "decision": "deny",
  "reason": "tool_not_allowed"
}

Важно проверить именно отказ до запуска инструмента. Файл после теста должен существовать:

test -f "$WORKDIR/logs/app.json" \
  && printf '%s\n' "OK: файл не удалён"

Отдельно проверьте разрешённый путь чтения, повторный вызов после исчерпания лимита и вызов после истечения срока. Для проверки срока создайте карточку с датой в прошлом и убедитесь, что причина — card_expired, а не общий access_denied.

Как понять, что ограничение действительно работает

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

  1. разрешённый вызов проходит только с указанным инструментом и ресурсом;
  2. вызов другого инструмента отклоняется до исполнения;
  3. превышение лимита отклоняется и фиксируется в журнале;
  4. просроченная карточка не восстанавливает доступ после перезапуска агента.

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

Типовые ошибки

Разрешение выдано роли, а не операции

Формулировка «агенту разрешён файловый доступ» слишком широка. Указывайте инструмент и действие отдельно: fs.read с операцией read не должен наследовать права fs.write или fs.delete.

Область задаётся слишком общим шаблоном

Путь вроде /workspace/** может включать конфигурацию, артефакты и секреты. Сузьте область до нужного каталога и формата файлов. Если библиотека сопоставления путей поддерживает неоднозначные шаблоны, протестируйте их отдельно.

Проверяется только срок

Свежая карточка всё ещё может разрешать опасный инструмент. Срок — одна из проверок, а не замена списку инструментов, операций и ресурсов.

Отказ существует только в интерфейсе агента

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

Логи раскрывают чувствительные данные

Записывайте метаданные решения: субъект, инструмент, ресурсный идентификатор, результат и причину. Не помещайте в журнал токены, содержимое файлов и персональные данные без отдельной политики хранения.

Ограничения подхода

Разрешения не исправляют уязвимый сам инструмент. Если fs.read неправильно нормализует путь, карточка с корректной областью может быть обойдена на уровне реализации. Поэтому проверка авторизации должна дополняться безопасной реализацией инструментов, нормализацией путей, изоляцией процесса и контролем исходящих сетевых соединений.

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

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

Итоговый чек-лист

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

Для следующего шага откройте раздел практических гайдов и сверяйте термины по глоссарию Agent Lab Journal.