Держать пароль к базе данных прямо в коде обычным текстом кажется удобным: написал строку подключения и работаешь. Но рано или поздно это играет против вас.
Сценариев утечки полно: демонстрация экрана на созвоне или запись урока - и пароль мелькнул при переключении вкладок. Скриншот с куском кода в чате поддержки. И самый частый случай - секрет попал в git. Репозиторий при этом может быть даже приватным: доступ к нему есть у коллег и подрядчиков.
Вывод простой: секреты в коде хранить нельзя. Разберёмся, что считать секретом, чем это грозит и как правильно хранить пароли, токены и ключи, чтобы не спалиться. Способов много - пройдём 13 подходов от простого к продвинутому.
Что вообще считается секретом
Секрет - любые данные, по которым можно получить доступ к чему-то вашему. Держать их в коде нельзя ни в каком виде:
- пароли к базам данных и сервисам;
- токены и ключи API (платёжные системы, маркетплейсы, облака);
- строки подключения, где пароль зашит внутри;
- приватные ключи, сертификаты, secret key приложений.
Приватный репозиторий не делает секрет безопасным. Как только пароль попал в коммит, он остаётся в истории git навсегда, даже если вы удалите его следующим коммитом. Поэтому задача - не дать секрету попасть в код вообще.
Чем грозит пароль в коде
Прежде чем перейти к правильным способам, зафиксируем риски. Пароль в коде утекает через:
- Запись экрана и скриншоты. Стрим, обучающее видео, демонстрация на созвоне - код с паролем попадает в кадр.
- Git-историю. Даже удалённый позже секрет остаётся во всех прошлых коммитах и у всех, кто клонировал репозиторий.
- Публикацию репозитория. Приватный проект однажды могут выложить в открытый доступ вместе со всей историей.
- Логи и бэкапы. Код с секретом расходится по CI, логам сборки и резервным копиям.
Теперь к тому, как делать правильно.
Хорошие практики хранения секретов
Единственно верного способа нет - выбор зависит от того, где и с чем вы работаете, от чего этот пароль и какая у вас инфраструктура. Разберём подходы по группам: от простого выноса секрета из кода до полного отказа от постоянного пароля.
Вынести секрет из кода: простые способы
1. Переменные окружения
Самый простой и популярный способ. Секрет хранится в настройках операционной системы или в CI/CD-платформе (GitHub Actions, GitLab CI, Jenkins), а код читает его оттуда. В самом коде пароля нет - только имя переменной.
На Python читаем переменные окружения так:
import os
password = os.getenv('DB_PASSWORD')
if password is None:
raise RuntimeError('Не задана переменная окружения DB_PASSWORD')
Если переменная не задана, скрипт упадёт с понятной ошибкой, но сам пароль нигде не засветится - ни в коде, ни в репозитории. Это разумный дефолт для большинства проектов.
2. Файл .env для локальной разработки
Задавать переменные окружения вручную на каждой машине неудобно. Для локальной разработки удобнее файл .env в корне проекта, куда складываются все секреты:
DB_PASSWORD=super_secret
API_TOKEN=y0_AgAAAA...
Читаем его через библиотеку python-dotenv:
from dotenv import load_dotenv
import os
load_dotenv()
password = os.getenv('DB_PASSWORD')
Ставится обычным pip:
pip install python-dotenv
Ключевое правило: файл .env всегда добавляется в .gitignore, чтобы он не попал в коммит. А в репозиторий кладётся шаблон .env.example с именами переменных без значений:
DB_PASSWORD=
API_TOKEN=
Новый разработчик клонирует проект, копирует .env.example в .env и вписывает свои значения локально. Так все знают, какие переменные нужны, но реальные секреты остаются только на машинах.
3. Конфиг вне репозитория с правами доступа
Секреты можно держать в отдельном конфиге, который лежит вне папки проекта - например, в ~/.config/myapp/secrets.ini или /etc/myapp/. В репозиторий такой файл не попадёт физически, а код читает его по абсолютному пути.
Важный нюанс - права доступа. Выставьте файлу режим, при котором его читает только владелец:
chmod 600 ~/.config/myapp/secrets.ini
Так секрет не увидят другие пользователи системы. Подход выручает на серверах и в скриптах, где не хочется тянуть лишние зависимости.
4. Хранилище секретов операционной системы (keyring)
У каждой ОС есть встроенное защищённое хранилище: Диспетчер учётных данных в Windows, Keychain в macOS, Secret Service в Linux. Пароль лежит в зашифрованном виде на уровне системы, а не в файле проекта.
В Python с ним работает библиотека keyring:
import keyring
# один раз сохранить
keyring.set_password('mydb', 'admin', 'super_secret')
# дальше только читать
password = keyring.get_password('mydb', 'admin')
Отлично подходит для локальных и десктопных приложений: пароль вводится один раз и дальше берётся из системного хранилища.
Централизованные менеджеры секретов
Когда секретов много и с ними работает команда, файлы и переменные окружения перестают масштабироваться. Нужен единый источник с правами доступа, аудитом и ротацией.
5. HashiCorp Vault
Корпоративный стандарт для хранения паролей и любых кредов. Внутри простой принцип: создаётся переменная, ей задаётся значение, а доступ раздаётся по правам - у одних сервисов есть права на чтение или изменение, у других нет.
Главная фишка Vault - динамические секреты. Он умеет генерировать пароли на лету и выдавать их на ограниченное время. Если злоумышленник украдёт такой пароль, через час тот уже истечёт. Vault берут, когда секретов много, ими пользуется команда и нужен аудит доступа.
6. Облачные менеджеры секретов
Если проект живёт в облаке, у провайдера обычно есть свой сервис для секретов: AWS Secrets Manager, Azure Key Vault, Google Secret Manager, Yandex Lockbox. Логика та же, что у Vault: секрет хранится в защищённом хранилище, приложение получает его по правам доступа (IAM), а не из файла. Плюс - тесная интеграция с остальной инфраструктурой облака и автоматическая ротация.
Рядом стоит KMS (Key Management Service) - сервис управления ключами шифрования. По схеме envelope-шифрования сам секрет шифруется ключом, который никогда не покидает KMS. Так устроено шифрование в облачных менеджерах под капотом.
7. SaaS-менеджеры секретов
Если своего Vault поднимать не хочется, есть готовые сервисы: Doppler, Infisical. Они хранят секреты у себя, синхронизируют их в окружения и CI, дают доступ по ролям и историю изменений. Настраиваются быстрее собственного Vault.
Сюда же - CLI пароль-менеджеров (1Password CLI, Bitwarden CLI): секрет лежит в вашем менеджере паролей, а скрипт подтягивает его в переменную окружения в момент запуска.
Секреты в контейнерах и оркестрации
8. Секреты Docker
Docker и Docker Compose умеют пробрасывать секреты в контейнер отдельным механизмом (docker secrets), а не через переменные образа. Секрет монтируется как файл во время работы контейнера и не остаётся в слоях образа - в отличие от ENV, значение которого видно в истории образа.
9. Секреты Kubernetes
В Kubernetes есть объект Secret, но по умолчанию его значения хранятся в etcd лишь в base64 (это кодировка, а не шифрование). Поэтому в проде включают шифрование etcd через KMS и настраивают RBAC по принципу минимальных прав.
Для команд удобнее не хранить секреты в кластере вовсе, а подтягивать из внешнего менеджера: External Secrets Operator синхронизирует их из Vault или облака, а Sealed Secrets позволяет держать зашифрованный секрет прямо в git и расшифровывать только внутри кластера.
Шифрование секретов рядом с кодом
10. Шифрование конфигурационных файлов
Компромисс между удобством и безопасностью. Конфиг с паролями лежит прямо в репозитории, но в зашифрованном виде, а расшифровывается только при деплое мастер-ключом, который хранится отдельно. Для этого есть SOPS, git-crypt и Ansible Vault. Удобно, когда хочется держать конфигурацию рядом с кодом, но не светить значения.
11. Подмена секретов на этапе сборки
В некоторых инструментах (Docker BuildKit, Webpack) секрет можно подставить только во время сборки образа, не зашивая его внутрь. Например, пароль нужен, чтобы поставить зависимости из приватного репозитория, а после сборки он удаляется и в финальный контейнер не попадает.
Лучший вариант - вообще без пароля
Самый надёжный секрет - тот, которого нет. Два подхода позволяют не хранить постоянный пароль в принципе.
12. IAM-роли, workload identity и короткоживущие токены
В облаке приложению можно не выдавать пароль вообще, а привязать к серверу или контейнеру роль доступа (IAM-роль, service account, workload identity). Тогда SDK сам получает временные креды, которые живут минуты и обновляются автоматически.
Например, при работе с AWS через boto3 на сервере с привязанной ролью ключи прописывать не нужно:
import boto3
# ключи не заданы: boto3 берёт временные креды из IAM-роли сервера
s3 = boto3.client('s3')
Красть тут нечего: постоянного секрета нет, а временный протухает сам. Это современный стандарт для облачных сервисов.
13. Интерактивный ввод в рантайме
Если скрипт запускает человек, пароль можно вообще не хранить, а спрашивать при запуске. Модуль getpass вводит его скрыто, без отображения на экране:
from getpass import getpass
password = getpass('Пароль к БД: ')
Секрет живёт только в оперативной памяти на время работы программы и никуда не записывается. Подходит для ручных операций и разовых скриптов.
Как не закоммитить секрет случайно
Даже с переменными окружения легко один раз забыться и вписать пароль прямо в код. Чтобы этого не случилось, есть автоматическая защита.
Pre-commit хуки. Инструменты gitleaks, trufflehog, git-secrets и detect-secrets встраиваются в git и проверяют каждый коммит на наличие похожих на секреты строк. Нашли токен - коммит не пройдёт. Настраивается один раз через pre-commit:
pip install pre-commit
Дальше в файле .pre-commit-config.yaml подключаете нужный сканер, и он срабатывает автоматически перед каждым коммитом.
Secret scanning на стороне хостинга. GitHub и GitLab сами сканируют репозитории на утёкшие ключи известных сервисов и присылают предупреждения. Это не заменяет pre-commit, но ловит то, что проскочило.
Правильный .gitignore. Добавьте туда .env, файлы с ключами (*.pem, *.key), локальные конфиги. Проще всего - взять готовый шаблон gitignore для своего языка.
Что делать, если секрет уже утёк
Если пароль или токен попал в git или куда-то ещё, главное правило одно: секрет нужно немедленно сменить (ротировать).
Многие думают, что достаточно удалить строку из кода или переписать историю git. Это ошибка. Удалить секрет из истории (через git filter-repo или BFG) полезно, но недостаточно: копии уже могли утечь, а сам пароль остаётся действующим. Поэтому порядок такой:
- Отзовите или смените утёкший секрет в сервисе - это обязательный шаг.
- Выпустите новый и положите его в переменные окружения или менеджер секретов.
- Только потом при желании чистите git-историю, чтобы старое значение не смущало.
Ротация закрывает проблему по существу: даже если старый секрет где-то всплывёт, он уже не работает.
Чек-лист
Коротко, что должно быть в проекте:
- В коде нет ни одного пароля, токена или ключа.
- Секреты читаются из переменных окружения или менеджера секретов.
- Файл
.envв.gitignore, в репозитории только.env.example. - Настроен pre-commit хук со сканером секретов.
- Для команды и продакшена - Vault, облачный менеджер или SaaS-сервис секретов.
- Где возможно - вместо пароля используется IAM-роль с временными кредами.
- Есть понимание, что делать при утечке: сначала ротация, потом всё остальное.
FAQ
Сколько всего способов хранить секреты?
Подходов много: переменные окружения, файл .env, конфиг вне репозитория, хранилище ОС (keyring), HashiCorp Vault, облачные менеджеры (AWS, Azure, GCP, Yandex Lockbox), SaaS-сервисы (Doppler, Infisical), секреты Docker и Kubernetes, шифрование конфигов (SOPS, git-crypt), подмена на этапе сборки, IAM-роли и интерактивный ввод. Выбор зависит от инфраструктуры.
Какой способ хранить секреты выбрать новичку?
Начните с переменных окружения и файла .env (с python-dotenv), добавив .env в .gitignore. Этого достаточно для большинства небольших проектов. Vault, облачные и SaaS-менеджеры нужны, когда над проектом работает команда.
Что такое переменные окружения простыми словами?
Переменные окружения - значения, которые хранятся в настройках операционной системы или CI, а не в коде. Программа читает их при запуске (в Python - через os.getenv), поэтому сам секрет в исходники не попадает.
Можно ли вообще не хранить пароль?
Да. В облаке приложению привязывают IAM-роль, и SDK получает временные креды сам, без постоянного пароля. А для ручных скриптов пароль спрашивают при запуске через getpass - он живёт только в памяти.
Секрет уже попал в git, что делать?
В первую очередь сменить (ротировать) сам секрет в сервисе - это главное. Чистка git-истории вторична: без ротации старый пароль остаётся рабочим, даже если убрать его из истории.
Заключение
Пароль в коде рано или поздно утекает - через запись экрана, скриншот или git. Держать секреты в коде нельзя, но способов обойти это масса: для старта хватит переменных окружения и файла .env, для команды - Vault, облачный или SaaS-менеджер, в контейнерах - секреты Docker и Kubernetes, а лучший вариант - вообще не хранить пароль и использовать IAM-роли с временными кредами. От случайного коммита защитит pre-commit хук со сканером. И помните главное правило на случай утечки: сначала ротация секрета, потом всё остальное.