AI · 9 мин чтения

Ключ OpenAI API в переменной окружения: как хранить и подключать без утечки

Ключ OpenAI API в переменной окружения хранить нужно по одной причине: в коде он рано или поздно уезжает туда, куда не должен. В репозиторий, в скриншот для чата поддержки, в архив для подрядчика, в браузер посетителя. Ниже я показываю, как настраиваю openai api ключ переменная окружения на своём компьютере, на обычном хостинге с PHP и в Python-проектах, и что делаю, если ключ всё же засветился.

Где хранить API-ключ OpenAI и почему не в коде

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

  • ключ вписан прямо в PHP или Python-файл, который потом коммитится в Git;
  • ключ лежит в JavaScript на странице, например в блоке с кодом на Tilda, и виден любому через инструменты разработчика;
  • файл с настройками лежит в публичной папке сайта и открывается по прямой ссылке;
  • ключ отправлен в переписке или на скриншоте, а потом остался в истории.

Переменная окружения закрывает первый и частично третий сценарии: код ссылается на имя переменной, а значение живёт отдельно от проекта. Второй сценарий она не лечит вообще. Если вы вызываете OpenAI из браузера, ключ окажется в браузере при любом способе хранения. Запрос к API всегда должен идти с сервера: с PHP-обработчика на вашем хостинге, из бота или из сценария n8n.

Стандартное имя переменной для ключа OpenAI это OPENAI_API_KEY. Официальные клиентские библиотеки OpenAI читают её сами, поэтому в коде ключ часто вообще не упоминается. Я придерживаюсь этого имени и в проектах без официальной библиотеки, чтобы не путаться.

Сравнение способов хранения

Способ Кто может увидеть ключ Когда подходит
Строка в коде Все, у кого есть доступ к репозиторию или архиву Нигде, даже для теста
Переменная окружения процесса Пользователи сервера с доступом к процессу Локальная разработка, VPS, контейнеры
Файл .env вне публичной папки Тот, у кого есть доступ к файловой системе Обычный хостинг, небольшие проекты
Раздел секретов в сервисе Участники рабочего пространства n8n, CI, облачные платформы

Как задать переменную среды для OPENAI_API_KEY на своём компьютере

Сначала в панели OpenAI создайте отдельный ключ под проект. Один ключ на всё сразу неудобен: когда придёт время отзывать, придётся перенастраивать все сервисы. Название ключа делайте понятным, например по имени сайта или бота, чтобы по списку было видно, что и где используется.

В Linux, macOS и Git Bash на Windows переменная задаётся командой export. Она живёт, пока открыто окно терминала:

export OPENAI_API_KEY="sk-ваш-ключ"
echo ${#OPENAI_API_KEY}

Вторая строка печатает длину значения, а не сам ключ. Так я проверяю, что переменная задана, и не выношу секрет на экран.

В PowerShell синтаксис другой. Для текущей сессии:

$env:OPENAI_API_KEY = 'sk-ваш-ключ'
$env:OPENAI_API_KEY.Length

Чтобы переменная сохранялась между сессиями на Windows, используют setx OPENAI_API_KEY "sk-ваш-ключ". Учтите, что уже открытые окна терминала и редактор кода новое значение не увидят, их нужно перезапустить. Эта ловушка отнимала у меня время не раз: переменная задана, а программа её не видит, потому что запущена из старого окна.

Для постоянной переменной в Linux и macOS строку с export добавляют в файл профиля оболочки, например ~/.bashrc или ~/.zshrc. Но держать там ключи нескольких проектов неудобно, поэтому для проектов я предпочитаю файл .env.

Файл .env и .gitignore: настройка секрета для проекта

Файл .env лежит в корне проекта и хранит пары «имя=значение». Программа читает его при старте и подставляет значения в переменные окружения. Содержимое:

OPENAI_API_KEY=sk-ваш-ключ

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

echo ".env" >> .gitignore
git status

В выводе git status файла .env быть не должно. Если он там есть, значит, Git его уже отслеживает, и просто добавить имя в .gitignore мало. Файл нужно убрать из индекса:

git rm --cached .env
git commit -m "Remove .env from tracking"

Это убирает файл из будущих коммитов, но в истории он остаётся. Проверить, попадал ли .env в историю, можно так: git log --all -- .env. Если команда вернула коммиты, ключ считайте скомпрометированным, даже если репозиторий приватный (к нему могут получить доступ подрядчики и сервисы-интеграции).

Второй файл, который я всегда кладу рядом, это .env.example с теми же именами и пустыми значениями. Он коммитится, и по нему любой разработчик понимает, какие переменные нужны проекту, не видя чужих секретов.

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

Ключ OpenAI в PHP на обычном хостинге

Большинство сайтов, с которыми я работаю, живут на обычном хостинге с PHP и MySQL. Консоли с export там чаще всего нет, а процессом веб-сервера вы не управляете. Поэтому схема другая: файл с ключом лежит на уровень выше публичной папки сайта (той, что открывается по адресу домена), и PHP читает его при каждом запросе.

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

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

<?php
$config = parse_ini_file(dirname(__DIR__) . '/.env');
$apiKey = getenv('OPENAI_API_KEY') ?: ($config['OPENAI_API_KEY'] ?? '');

if ($apiKey === '') {
    http_response_code(500);
    exit('Ключ не задан');
}

$ch = curl_init('https://api.openai.com/v1/models');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
    ],
    CURLOPT_TIMEOUT => 20,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo $status === 200 ? 'Ключ работает' : 'Ошибка ' . $status;

Здесь сначала проверяется настоящая переменная окружения через getenv, а потом файл. Так один и тот же код работает и на сервере, где переменные задаёт панель управления, и на обычном хостинге с файлом. Функция parse_ini_file читает простые строки вида «имя=значение», для ключа этого достаточно. Если файла нет, она вернёт false и выведет предупреждение, поэтому на боевом сайте файл должен лежать на месте. Статус 200 означает, что OpenAI принял ключ, статус 401 говорит о неверном или отозванном ключе.

Отдельно про Tilda. Если на сайте нужен ответ от нейросети, например подсказка в форме или чат, JavaScript на странице обращается не к OpenAI, а к вашему PHP-обработчику. Обработчик добавляет ключ и пересылает запрос. В коде блока Tilda ключа нет вообще. Заодно в таком обработчике удобно ограничить число запросов с одного IP, иначе посетитель может сжечь ваш лимит. Это то, что я закладываю в любую подобную интеграцию. Если нужна интеграция OpenAI в сайт или бота с такой защитой, это моя типовая задача.

Подключение ключа в Python, aiogram-боте и n8n

Python я беру там, где он действительно нужен: для ботов на aiogram и для парсинга. Для обычного сайта на PHP хватает. В Python-проекте схема та же: .env плюс библиотека, которая загружает его в окружение. Установка:

pip install openai python-dotenv

И пример, который проверяет ключ тем же запросом списка моделей:

from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()

client = OpenAI()
models = client.models.list()
print('Ключ работает, моделей доступно:', len(models.data))

Обратите внимание: ключ в коде не упоминается. Клиент OpenAI() сам берёт значение OPENAI_API_KEY из окружения. Вызов load_dotenv() должен выполняться до создания клиента, иначе переменная ещё не загружена.

В aiogram-боте у меня в .env лежат сразу две переменные: токен Telegram-бота и ключ OpenAI. Читаются они одинаково через os.environ. Токен бота такой же секрет, как и ключ OpenAI: кто его получил, тот управляет ботом. Поэтому правила для них одинаковые.

В n8n ключ в узлы вставлять руками не нужно. Для этого есть раздел Credentials: вы создаёте учётные данные один раз, а в узлах выбираете их из списка. Ключ хранится в зашифрованном виде внутри n8n, и при экспорте сценария в JSON он не попадает в файл, туда выгружается только ссылка на учётные данные. Это важно, когда вы отправляете сценарий коллеге или выкладываете его в публичный репозиторий. Если вы развернули n8n на собственном сервере, помните, что это отдельный VPS, который нужно обновлять, продлевать на нём сертификат и оплачивать каждый месяц, а для задач на обычном хостинге такой вариант не подходит.

Утечка ключа OpenAI: ротация и что проверить после

Если ключ попал в репозиторий, в чат или на скриншот, порядок действий у меня такой:

  1. Отзываю скомпрометированный ключ в панели OpenAI. Это первое и самое срочное действие, всё остальное потом.
  2. Создаю новый ключ с другим названием и записываю его в .env или в окружение сервера.
  3. Перезапускаю процессы, которые читали старое значение. В PHP на хостинге перезапуск не нужен, файл читается заново при каждом запросе, а в боте процесс нужно остановить и запустить снова.
  4. Просматриваю статистику использования за период утечки. Лишние запросы или рост расходов говорят о том, что ключом уже пользовались.
  5. Чищу историю Git, если ключ был в коммите. Но чистка истории вторична: пока ключ не отозван, она ничего не защищает.

После этого я добавляю профилактику. Во-первых, отдельный ключ на каждый проект, чтобы утечка затрагивала один сервис. Во-вторых, лимит расходов в панели OpenAI, который останавливает запросы при превышении суммы. Конкретные возможности настройки прав и лимитов зависят от текущего интерфейса, поэтому смотрите их в личном кабинете. В‑третьих, локальную проверку перед коммитом: достаточно посмотреть вывод git diff --staged и убедиться, что в нём нет строк, начинающихся с sk-.

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

Условия и возможности OpenAI, включая управление ключами и лимитами, меняются, поэтому перед запуском проекта я уточняю их в документации самого сервиса.

Частые вопросы

Можно ли хранить ключ OpenAI в файле wp-config.php?

Можно, если сайт на WordPress и файл не отдаётся браузером. Ключ задаётся константой через define и читается в коде плагина или темы. Но не добавляйте wp-config.php в Git и не отправляйте его в архивах. Вариант с .env выше публичной папки аккуратнее, потому что секрет не смешивается с остальными настройками сайта.

Нужно ли шифровать значение переменной окружения?

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

Почему программа не видит переменную, хотя я её задал?

Чаще всего причина одна из трёх: программа запущена из окна терминала или редактора, открытого до установки переменной; в Python load_dotenv() вызван после создания клиента; в .env есть лишние пробелы или кавычки вокруг значения. Проверьте длину значения командой из начала статьи, это быстрее всего показывает, что именно не так.

Можно ли вызывать OpenAI напрямую из JavaScript на странице?

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

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно