Безопасность ИИ: секреты, файлы .env и права доступа для агентов-программистов
Модель угроз в одном абзаце
ИИ-агент для программирования запускает команды оболочки с вашими правами и читает ваши файлы в свой контекст. Всё, что он прочитал, — включая секреты, — дальше уходит в вывод инструментов, транскрипты сессий, снимки файловой системы, отладочные логи, общие ссылки и экспортированные сессии, а на бесплатных тарифах, возможно, и в обучение моделей. Поэтому безопасность с ИИ-ассистентами — это две дисциплины: не пускать секреты в контекст агента вообще и давать каждому агенту только те права, которые нужны его роли, как в материалах «Создание и развитие агентов» и «Три модели, один проект».
Как aredel разделяет секреты сегодня
В этом репозитории конфигурация без секретов уже отделена от настоящих секретов. Оба файла ниже намеренно хранятся в git; пароля рядом с ними нет:
container/tools/deploy/ ├── site.env ├── site.env.example ├── deploy-ftp.sh └── DEPLOY_FTP.md ~/.config/aredel/ └── ftp-creds.env
container/tools/deploy/site.env
~/.config/aredel/ftp-creds.env
Правила, которых держится эта схема, проверены в репозитории:
- site.env хранится в git и не содержит секретов. Его собственный заголовок запрещает пароли. Параметры подключения повторяют секретный файл без пароля.
- Настоящий пароль лежит вне репозитория в ~/.config/aredel/ftp-creds.env, chmod 600, и игнорируется git по шаблону *-creds.env.
- Порядок приоритета: флаги CLI, затем файл creds, затем SITE_ENV, затем встроенные значения — а deploy-ftp.sh печатает предупреждение, если FTP_PASSWORD появился в SITE_ENV, и ловит именно ту ошибку, когда секрет вставили в отслеживаемый файл.
- Публичные id — не секреты. Идентификаторы аналитики, видимые в HTML страницы, не нуждаются в защите; не тратьте на них средства контроля доступа.
Что может пойти не так
- Обучение на бесплатном тарифе. Бесплатная модель OpenCode может быть тарифом для участников: запросы и ответы могут обучать будущие модели. Один вставленный пароль или запись клиента уже не отозвать. В этом репозитории так и случилось: «Как пароль FTP утёк в бесплатную модель ИИ».
- .env прочитан в контекст. Агент один раз читает файл creds ради законного вопроса о деплое, и секрет остаётся в транскрипте, снимках и любом session export или общей ссылке, сделанных позже.
- Промпт-инъекция из содержимого инструментов. Веб-страницы, результаты MCP и вывод браузера — недоверенные данные, а не инструкции. Загруженная страница может велеть агенту вынести файл одной командой загрузки — и агент с разрешённой оболочкой подчинится.
- Оболочка с вашими правами. Агент, которому разрешено запускать произвольную оболочку, может читать ~/.config, перечислять процессы с учётными данными в аргументах и отправлять что угодно куда угодно. Каждое широкое правило для оболочки — постоянное разрешение на вынос данных.
- Секреты в выводе инструментов. env, set, отладочные логи и дампы ошибок печатают секреты в результаты, которые агент потом цитирует в разговоре.
- Залипшие подтверждения. Одно небрежное «Allow always» на шаблон команды оболочки остаётся проектным правилом и молча одобряет последующие варианты.
- Ущерб от одного ключа. Один ключ провайдера для CI, скриптов деплоя и экспериментов означает, что одна утечка отзывает всё. Привычка OpenRouter задавать лимиты на каждый ключ существует именно для этого.
Права доступа, которые ограничивают ущерб
Позиция «запрещено по умолчанию» для OpenCode, из рабочей конфигурации трио в корне этого репозитория. Сначала глобальные правила, потом исключения для ролей — побеждает последнее совпадение. Файл — opencode.jsonc в корне репозитория:
vi opencode.jsonc
{ "$schema": "https://opencode.ai/config.json", "permissions": [ { "action": "shell", "resource": "*", "effect": "ask" }, { "action": "shell", "resource": "git push *", "effect": "deny" }, { "action": "shell", "resource": "sudo *", "effect": "deny" }, { "action": "read", "resource": "*.env", "effect": "deny" }, { "action": "read", "resource": "*creds*", "effect": "deny" } ] }
Роли из «Четыре готовых агента», ужесточённые для секретов:
| Агент | Читает .env | Правка | Оболочка | Почему |
|---|---|---|---|---|
| triage на бесплатной модели OpenCode | deny | deny | deny | Бесплатный тариф не должен касаться секретов вообще |
| builder на Luna | deny | allow | ask, тесты разрешены заранее | Пишет код, ничего не деплоит и не читает creds |
| reviewer на Opus 5.5 | deny | deny | deny | Судит диффы; находки упоминают работу с секретами, не цитируя значения |
Вопросы о деплое идут к человеку, который запускает deploy-ftp.sh, а не к агенту с доступом к файлу creds. Командные запреты, которые проекты не могут переопределить, живут в policies, а не в permissions — например, глобальный запрет на tool.use shell:sudo *. Пересматривайте залипшие правила «Allow always» раз в квартал и удаляйте те, которые никто не может обосновать. Какие правила держат, какие нет и проверенный пример «до и после» — в материале «Deny-правила для OpenCode и Claude Code».
Гигиена секретов, которая переживает ИИ
- Отделяйте отслеживаемую конфигурацию от секретов точно как в схеме aredel: site.env в коммите, site.env.example как шаблон, настоящие значения вне репозитория с chmod 600.
- Один ключ на задачу. CI, деплой и эксперименты получают каждый свой ключ с минимальным лимитом, который разрешает провайдер, так что одна утечка стоит одного отзыва.
- Меняйте по подозрению, а не по расписанию. После любого транскрипта, экспорта или ссылки, которые могли увидеть секрет, смените его — считайте, что значение уехало.
- Работа с секретами идёт локально или на платном тарифе. Всё, что касается creds, использует Ollama по материалу «Ollama для начинающих» или платную модель без хранения данных, но никогда бесплатный тариф для участников.
- Экспортируйте с очисткой. opencode session export --sanitize вымарывает транскрипты перед тем, как они покинут машину; по умолчанию не делитесь ничем.
- Следите за счётом. opencode stats --cost --models раз в неделю плюс месячные лимиты в Console — внезапный скачок тоже сигнал вторжения.
Ловить утечки до коммита
Честный пробел: сегодня в этом репозитории нет pre-commit-хука, который ищет секреты, — php -l, PHPCS и проверки переносов строк смотрят код, а не учётные данные. Пока его нет, ручной барьер — это grep по проиндексированным изменениям на известные шаблоны перед каждым коммитом:
git diff --cached | grep -n -i "FTP_PASSWORD\|API_KEY\|BEGIN.*PRIVATE KEY" | grep -v "example\|TEMPLATE\|secret-here"
Проверяйте каждое совпадение вручную: заглушки в файлах *.example — это нормально, настоящие значения — нет. Надёжное решение — хук-сканер (gitleaks или аналог), добавленный в .pre-commit-config.yaml; он записан как последующая работа, а не как повод пропустить ручной grep сейчас.
Чек-лист рисков для этого репозитория
| Риск | Состояние в aredel | Действие |
|---|---|---|
| Секреты в отслеживаемых файлах | Разделение сделано, скрипт деплоя предупреждает | Держать разделение; запускать grep по проиндексированным изменениям |
| Агент читает файл creds | Запрещено в корневой конфигурации для всех ролей | Проверить через opencode debug config |
| Утечка через обучение на бесплатном тарифе | Triage запрещает правки и оболочку, кроме навыка map | Никогда не отправлять задачи с creds на бесплатную модель |
| Промпт-инъекция через инструменты | Оболочка по умолчанию спрашивает | Оставить ask; никогда не выбирать Allow always для оболочки |
| Поиск секретов перед коммитом | Отсутствует | Сейчас ручной grep, дальше хук-сканер |
| Ущерб от одного ключа | Creds вне репозитория, одна задача деплоя | Отдельные ключи на каждую новую задачу |
Официальные источники:
- opencode.ai/v2/docs/permissions
- opencode.ai/v2/docs/policies
- docs.anthropic.com data usage
- platform.openai.com your data
Автор статьи: Клавдия Кондакова