Как хранить ключи проекта в .env и не отправить их в Git
Тому, кто собирает сайт или бота с внешними API
Проверки файла с ключами до коммита и выкладки
7 мин
Ключ вставлен в код ради быстрого запуска и попадает в чужие копии проекта
Секрет хранится отдельно, доступен только серверной части и проверен перед публикацией
Рабочий порядок для .env: отделить публичные настройки от секретов, проверить Git, права файла и передачу ключа приложению.
Как хранить ключи проекта в .env, если над сайтом работает ИИ‑агент? Файл удобен, но его имя само по себе не защищает секрет. Нужно решить, какой процесс читает значение, кто может открыть файл, попадёт ли оно в браузер и что окажется в истории Git. Я использую этот порядок в проектах с Next.js, Python-ботами и внешними API. Общий обзор защиты сайта есть в материале о безопасности; здесь только путь одного ключа.
Как разделить публичную настройку и секрет
Адрес открытого сайта, идентификатор счётчика и имя публичного проекта можно показать браузеру. Ключ оплаты, токен бота, пароль базы и ключ провайдера ИИ нужны серверу. Если код исполняется в браузере, пользователь может изучить отправленные скрипты. По официальной справке Next.js, проверенной 25 октября 2026 года, переменные с префиксом NEXT_PUBLIC_ встраиваются в браузерную сборку. Это подходящее место только для данных, которые разрешено публиковать.
Запишите для каждого значения владельца и потребителя. Например, серверный обработчик принимает сообщение и обращается к API по ключу; браузер получает только готовый ответ. Если фронтенду понадобился ключ провайдера, это сигнал изменить маршрут запроса. Для Vite публичные переменные с VITE_ тоже нельзя использовать для секретов. В моих проектах фронтенд и бэкенд часто разделены: у Radomir CRM интерфейс на React/Vite, а сервер на FastAPI. Этот стек показывает, почему нужно проверить обе стороны передачи, прежде чем добавлять переменную в сборку.
Перед тем как назвать переменную, проверьте её путь:
- найдите процесс, которому нужен ключ;
- отделите ответ пользователю от запроса к провайдеру;
- проверьте, что переменная не входит в браузерную сборку;
- запишите, где будет храниться настоящее значение.
| Значение | Где используется | Как хранить |
|---|---|---|
| Название публичного сайта | Интерфейс | В открытой настройке |
| Ключ провайдера ИИ | Серверный запрос | В закрытом окружении |
| Пароль базы | Сервер приложения | В закрытом окружении |
| Идентификатор аналитики | Браузер | В публичной настройке |
Что положить в проект вместо настоящего ключа
Для команды достаточно файла .env.example с именами переменных и заведомо фиктивными значениями. Он объясняет, какие настройки нужны для запуска, но не содержит действующих доступов. Настоящий ключ держите только в .env: локально и в закрытой папке сервера. Не дублируйте его в crontab, *.sh или .env.bak. Задания по расписанию и службы читают тот же закрытый файл, а копий с другим расширением не создают.
Пример ниже условный. Значение ваш_ключ не работает и не является настоящим токеном. Передавать такой файл в коммит можно только после проверки, что в нём нет реальных данных.
AI_PROVIDER_KEY=ваш_ключ
DATABASE_URL=адрес_тестовой_базыНе пишите в коде запасной пароль на случай отсутствия переменной. Если обязательного секрета нет, приложение должно остановиться с понятной ошибкой при запуске. Иначе тестовая строка вроде dev‑pass может неожиданно стать рабочим способом входа на сервере. Сам текст ошибки не должен выводить секрет. Достаточно имени отсутствующей переменной и инструкции, где её задать.
Как проверить Git до коммита
Правило .gitignore влияет на новые неотслеживаемые файлы. Оно не убирает секрет, который уже попал в историю. Документация Git прямо отделяет эти ситуации. Проверьте и правило игнорирования, и список файлов, уже известных Git. Команды ниже не печатают содержимое ключа, но могут показать имена файлов; не копируйте в публичный чат вывод со своими внутренними путями.
git check-ignore -v --no‑index .env
git ls‑files '.env' '.env.*'
git log --all -- .env
git status --short
gitleaks git .
gitleaks dir .Первая команда должна показать правило, которое закрывает настоящий файл. Вторая выявит уже отслеживаемые файлы; допустимый .env.example проверяйте отдельно по содержимому. git log покажет попадание .env в историю. gitleaks git . проверит историю, gitleaks dir . проверит текущие файлы до коммита. Эти команды соответствуют документации gitleaks, проверенной 26 октября 2026 года. Если настоящий ключ уже был закоммичен, одного удаления файла недостаточно: перевыпустите ключ у провайдера, оцените доступ к истории и разберите дальнейшие действия по регламенту проекта.
Кто может читать файл на сервере
Сервису нужен доступ к переменной, посторонним пользователям сервера не нужен. В моих правилах безопасности приложение запускается от отдельного пользователя, .env получает права 600, а служба не работает от root. Это проверка доступа на уровне файла и процесса. Она не заменяет ограничения прав самого ключа у провайдера: для одного проекта выдавайте отдельный ключ с минимальными возможностями и лимитом расходов.
chmod 600 .env
ls -l .env
systemctl show malyshev -p UserКоманда chmod применима к вашему настоящему файлу на системе с POSIX-правами. В systemctl show замените malyshev именем своей службы и проверьте отдельного пользователя. Он должен иметь доступ к .env; другие пользователи сервера его не читают. Для службы укажите User=, EnvironmentFile= с закрытым путём к .env и NoNewPrivileges=true. Пример фрагмента unit-файла:
[Service]
User=имя_пользователя_службы
EnvironmentFile=/закрытая/папка/проекта/.env
NoNewPrivileges=trueНе переносите .env вместе с публичной папкой сайта. После таких запросов из истории выше проверьте HTTP-ответ сами. curl -I https://сайт/.env должен вернуть 404. Любой другой ответ разберите до выкладки.
Какие границы дать ИИ‑агенту
Запретите агенту читать .env и другие секретные файлы в настройках его доступа. Например, для разрешений Claude Code добавьте "deny": ["Read(./.env)"]. Для задачи «добавить отправку письма» ему достаточно имени переменной, формата ответа API и фиктивного примера. Настоящее значение подставляет человек через закрытый .env. Если агент просит секрет ради генерации кода, дайте ему фиктивное значение и точный формат ответа API.
При проверке diff смотрите не только новые строки с ключом. Секрет может попасть в текст ошибки, журнал, снимок экрана, команду запуска или клиентскую переменную. Проверяйте, что сервер возвращает пользователю только результат действия. Исходный ответ провайдера может содержать служебные сведения. Для Python-бота это особенно важно: диагностические сообщения часто уходят в чат администратора и могут включать данные запроса.
| Этап | Вопрос | Проверка |
|---|---|---|
| Разработка | Где лежит настоящее значение? | Только в закрытом .env |
| Сборка | Попало ли оно в браузер? | Нет секретных NEXT_PUBLIC_ и VITE_ |
| Сервер | Кто читает файл? | Права файла и пользователь службы |
| Сбой | Что попадёт в журнал? | Только безопасная диагностика |
Как проверить рабочий путь после настройки
На локальной копии запустите сервис без обязательной переменной: он должен отказать при старте. Затем задайте тестовое значение и проверьте, что код доходит до ожидаемого вызова. На сервере выполните отдельную проверку службы и одного безопасного сценария, не выводя ключ в терминал. Если ключ меняется, старое значение отключайте после подтверждения работы нового, а запись о ротации храните вне кода.
В Deyvo вместе живут веб‑приложение, FastAPI и Telegram-бот. Это пример проекта, где один секрет нужен конкретному процессу. Всем компонентам подряд давать его незачем. Раздавайте настройки адресно: токен бота нужен боту, ключ базы только тем процессам, которые с ней работают. Так утечка одного окружения затронет меньше системы.
Частые вопросы
Можно ли коммитить .env.example?
Да, если он содержит только имена переменных и фиктивные значения. Проверяйте содержимое перед каждым изменением примера.
Достаточно ли добавить .env в .gitignore?
Для нового файла это помогает. Если секрет уже отслеживался Git, правило его из истории не удалит; действующий ключ нужно заменить.
Можно ли использовать NEXT_PUBLIC_ для ключа API?
Нет, если ключ секретный. Next.js включает такую переменную в браузерную сборку. Вызов к провайдеру должен идти через сервер.
Почему служба не видит переменную после перезапуска?
Проверьте путь к файлу, пользователя службы и способ передачи окружения в её настройках. Не печатайте значение при диагностике.
Где хранить ключи нескольких проектов?
По отдельности, с минимальными правами на каждый проект и понятным владельцем. Один общий ключ усложняет замену и учёт расходов.
На обучении разбираю эту проверку на живом пути: агент меняет код, человек подставляет секрет, а перед выкладкой оба проверяют, что значение не ушло в Git и браузер.