
Переменные окружения и секреты в Python
Передаём скрипту настройки через окружение, отличаем обязательные значения от необязательных, преобразуем типы и не раскрываем токены в коде и логах.
Содержание
Токен API, адрес сервиса и тайм-аут меняются между компьютерами и окружениями. Если записать их в исходный файл, секрет может попасть в Git, а для смены адреса придётся редактировать программу. Переменные окружения передают значения процессу при запуске и оставляют код неизменным.
Окружение процесса — набор пар «имя — строковое значение», который операционная система передаёт запущенной программе. Это канал конфигурации, а не шифрование. Получивший достаточный доступ к процессу или службе может прочитать эти значения.
В этом уроке токен используется только как учебная строка и не отправляется в сеть. Правила журналирования из материала о logging особенно важны: секрет нельзя подставлять в сообщение об ошибке.
Передайте значение только текущей сессии терминала
Сначала создайте env_demo.py по следующему разделу. Затем вернитесь к этому месту и выполните блок для своей операционной системы: команды задают переменные и сразу запускают готовый файл.
В PowerShell задайте учебное значение и запустите Python в том же окне:
$env:APP_ENDPOINT = "https://example.com/"
$env:APP_TIMEOUT = "5"
$env:APP_TOKEN = "replace-with-test-value"
python env_demo.py
В Bash на Linux или macOS те же три значения задаются командой export, после чего файл запускается в этой же сессии терминала:
export APP_ENDPOINT='https://example.com/'
export APP_TIMEOUT='5'
export APP_TOKEN='replace-with-test-value'
python env_demo.py
Команды меняют окружение текущей сессии и запускаемых из неё процессов. Они не записывают значение в код проекта. Закройте окно терминала — переменные этой сессии исчезнут. Не подставляйте реальный токен в историю общей или записываемой терминальной сессии.
Отличайте обязательное значение от необязательного
Создайте env_demo.py. Адрес и токен считаются обязательными, а тайм-аут имеет документированное значение по умолчанию:
import os
def required_env(name):
try:
value = os.environ[name]
except KeyError as error:
raise ValueError(f"Не задана переменная окружения {name}") from error
if not value.strip():
raise ValueError(f"Переменная окружения {name} пуста")
return value
endpoint = required_env("APP_ENDPOINT")
token = required_env("APP_TOKEN")
timeout_text = os.environ.get("APP_TIMEOUT", "5")
print(f"Адрес сервиса: {endpoint}")
print(f"Токен передан: {'да' if token else 'нет'}")
print(f"Тайм-аут: {timeout_text}")
После задания переменных ожидается адрес, подтверждение наличия токена и строка 5. Сам токен не печатается. Удалите APP_TOKEN из сессии и повторите запуск: программа должна завершиться с сообщением о конкретном отсутствующем имени.
Обращение os.environ[name] вызывает KeyError, если обязательного имени нет. Метод get подходит для необязательного значения только тогда, когда безопасное и предсказуемое значение по умолчанию действительно существует.
Преобразуйте строки до основной работы
Переменная окружения всегда приходит строкой. Добавьте функцию разбора положительного числа и используйте её для тайм-аута:
def positive_int(name, value):
try:
number = int(value)
except ValueError as error:
raise ValueError(f"{name} должна быть целым числом") from error
if number <= 0:
raise ValueError(f"{name} должна быть больше нуля")
return number
timeout = positive_int("APP_TIMEOUT", timeout_text)
print(f"Проверенный тайм-аут: {timeout} секунд")
Задайте APP_TIMEOUT=быстро и убедитесь, что ошибка возникает до HTTP-запроса или изменения файлов. Для логического флага заранее определите допустимые строки, например true и false; выражение bool("false") вернёт True, потому что непустая строка считается истинной.
Список значений тоже требует формата. Если адреса разделяются запятыми, объясните, допустимы ли пробелы и пустые элементы. Для сложной вложенной конфигурации лучше использовать проверяемый JSON-файл, а секрет передать отдельно.
Не превращайте .env в скрытый источник истины
Имя .env стало распространённой договорённостью, но os.environ не читает такой файл автоматически. Дополнительная библиотека может загрузить его локально, однако реальный .env должен быть исключён из Git, резервных копий общего доступа и каталога, который публикует веб-сервер.
В репозитории допустим .env.example только с именами и безопасными заглушками:
APP_ENDPOINT=https://example.com/
APP_TIMEOUT=5
APP_TOKEN=replace-me
Этот файл объясняет состав конфигурации, но не предоставляет рабочий доступ. Значения вроде replace-me должны приводить к отказу в рабочем окружении, если программа может по ошибке принять их за настоящий токен.
Проверьте историю Git, если секрет когда-либо был закоммичен. Простого удаления из последнего файла недостаточно: значение остаётся в истории и должно быть отозвано у поставщика сервиса.
Передавайте токен только нужному запросу
Для HTTP-клиента сформируйте заголовок непосредственно перед созданием Request, но не сохраняйте его в журнале:
from urllib.request import Request
headers = {
"Accept": "application/json",
"Authorization": f"Bearer {token}",
"User-Agent": "automation-tool/1.0",
}
request = Request(endpoint, headers=headers, method="GET")
Фрагмент только создаёт объект запроса и ничего не отправляет. Не печатайте headers: в словаре находится полный токен. При перенаправлении на другой домен убедитесь, что клиент не передаёт чувствительный заголовок неожиданному получателю; для строгого сценария до отправки проверьте имя узла, то есть доменную часть адреса.
На VPS переменные службы обычно задают в защищённой конфигурации systemd или менеджере секретов с минимальными правами чтения. Не помещайте секрет прямо в общий unit-файл, если он доступен всем пользователям. Способ хранения зависит от окружения, но принцип один: доступ получает только процесс, которому значение действительно нужно.
Официальный справочник os.environ описывает отображение окружения процесса. Следующий урок применит конфигурацию, аргументы, журнал и тестируемые функции в сортировщике файлов.
Самопроверка
Проверьте, что материал усвоен
Ответьте на все вопросы. Результат сохранится только в этом браузере и будет учтён в статистике прочитанных материалов.
Разбираем коротко
Частые вопросы
Является ли переменная окружения зашифрованным хранилищем?
Нет. Она отделяет значение от исходного кода, но доступна процессу, его потомкам и пользователям с достаточными правами. Для рабочего сервера нужен подходящий менеджер секретов или защищённая конфигурация службы.
Загружает ли Python файл .env автоматически?
Нет. Стандартный os.environ читает окружение процесса. Для .env нужен отдельный загрузчик или явный код, а сам файл всё равно нельзя коммитить с реальными секретами.
Почему значения окружения нужно преобразовывать?
Они приходят как строки. Тайм-аут, флаг или список нужно разобрать и проверить до основной работы программы.


