Пошук уроків, статей та іншого контенту
Запобігаємо витоку секретів, очищуємо історію та налаштовуємо перевірки безпеки перед публікацією змін.
Секретами називають дані, які не можна публічно розголошувати:
ключі API;
паролі;
токени доступу;
приватні ключі SSH;
рядки підключення до бази даних;
секрети для CI/CD;
файли сертифікатів і ключі підпису.
Git зберігає не лише поточний стан файлів, а всю історію змін. Тому секрет, який випадково потрапив у коміт, може залишитися доступним навіть після видалення файлу з останньої версії.
Якщо секрет потрапив до репозиторію, його потрібно вважати скомпрометованим. Простого видалення файлу недостатньо.
Перші дії після витоку:
Відкликати або замінити секрет у відповідному сервісі.
Перевірити журнали використання ключа.
Повідомити команду про необхідність оновити локальні копії.
Очистити секрет з історії Git.
Додати захист, який не дозволить повторити помилку.
Конфігурацію з секретами зазвичай зберігають у змінних середовища. Для локальної розробки можна використовувати файл .env, але його не слід додавати до Git.
# Локальна конфігурація із секретами
.env
.env.*
!.env.example
# Приватні ключі та сертифікати
*.pem
*.key
*.p12
# Локальні файли інструментів
.DS_StoreПравило .env.* ігнорує файли на кшталт .env.local та .env.production, але виняток !.env.example дозволяє додати безпечний шаблон.
Приклад .env.example:
# Приклад змінних без реальних значень
DATABASE_URL=
API_TOKEN=Такий файл показує назви необхідних змінних, але не містить їхніх значень.
У програмі секрети читаються зі змінних середовища. Наприклад, у Node.js:
const apiToken = process.env.API_TOKEN;
if (!apiToken) {
throw new Error('Змінна API_TOKEN не налаштована');
}
console.log('Токен налаштовано');У .env на локальній машині можуть бути реальні значення:
API_TOKEN=local-token-for-developmentАле цей файл має залишатися поза індексом Git.
git check-ignore -v .envКоманда покаже правило з .gitignore, яке відповідає за ігнорування файлу.
Важливо: .gitignore діє на неіндексовані файли. Якщо файл уже був доданий до коміту, додавання його до .gitignore не припинить відстеження.
Щоб прибрати файл із майбутніх комітів, але залишити його локально:
git rm --cached .env
git commit -m "Stop tracking local environment file"Це не видаляє секрет з попередніх комітів. Воно лише прибирає файл із поточного стану репозиторію.
Перед комітом варто перевіряти:
які файли будуть додані;
які рядки змінюються;
чи немає в них токенів, паролів або приватних ключів;
чи не додано файл із секретами через git add -f.
Основні команди:
git status
git diff
git diff --cachedgit diff --cached показує саме те, що потрапить до наступного коміту.
Корисно перевірити список файлів у коміті:
git diff --cached --name-onlyОкрему увагу потрібно приділяти великим конфігураційним файлам, JSON-файлам і скриптам CI. Секрет може бути записаний не лише у .env, а й безпосередньо в коді:
const token = 'real-production-token';Git дозволяє запускати локальний скрипт перед створенням коміту. Файл .git/hooks/pre-commit не додається до репозиторію автоматично, але його можна використовувати на локальній машині.
Приклад перевірки staged-змін:
#!/usr/bin/env bash
set -euo pipefail
# Отримуємо додані рядки з підготовлених до коміту змін
staged_content="$(git diff --cached --no-color --unified=0 --diff-filter=ACMRT)"
# Перевіряємо типові назви секретних змінних
if printf '%s\n' "$staged_content" | grep -Eiq \
'(^|\+).*(api[_-]?key|api[_-]?token|secret|password|private[_-]?key)[[:space:]]*[:=]'; then
echo "Коміт заблоковано: знайдено можливий секрет."
echo "Перевірте staged-зміни командою: git diff --cached"
exit 1
fi
# Перевіряємо поширені формати приватних ключів
if printf '%s\n' "$staged_content" | grep -Eq \
'BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY'; then
echo "Коміт заблоковано: знайдено приватний ключ."
exit 1
fi
echo "Перевірку секретів пройдено."Зробіть файл виконуваним:
chmod +x .git/hooks/pre-commitТакий скрипт є лише базовим захистом. Він може давати хибні спрацювання і не гарантує виявлення всіх форматів секретів. Перевірку потрібно доповнювати правилами, які відповідають конкретному проєкту.
Якщо коміт ще не опубліковано, ситуацію можна виправити локально.
Спочатку відкличте або замініть секрет. Потім видаліть його з файлу, додайте файл до .gitignore і змініть останній коміт:
git add .gitignore path/to/config.js
git commit --amendЯкщо потрібно прибрати файл із останнього коміту, але залишити його на диску:
git rm --cached path/to/secret-file
git commit --amendПеред публікацією перевірте результат:
git show --stat
git showЯкщо коміт уже був відправлений до віддаленого репозиторію, одного git commit --amend недостатньо. Потрібно також оновити віддалену гілку, а це може переписати її історію.
Видалення файлу в новому коміті не прибирає його зі старих комітів:
git rm --cached .env
git commit -m "Remove environment file"Старі версії .env усе ще можна знайти через історію.
Для очищення історії використовують git filter-repo. Перед операцією створіть резервну копію репозиторію та переконайтеся, що команда розуміє наслідки переписування історії.
git filter-repo --path .env --invert-pathsЩоб видалити кілька шляхів:
git filter-repo \
--path .env \
--path config/production.json \
--invert-pathsПісля очищення перевірте історію:
git log --all -- .env
git log --all -- config/production.jsonЯкщо команда не повертає комітів із цими файлами, вони більше не присутні за вказаними шляхами в переписаній історії.
Якщо секрет був частиною файлу разом із потрібними даними, можна замінити його значення. Створіть файл replacements.txt:
real-production-token==>REMOVED_SECRETПотім виконайте:
git filter-repo --replace-text replacements.txtФайл із реальним секретом після операції потрібно видалити або захистити. Не додавайте його до репозиторію.
Після перевірки очищену історію потрібно відправити до віддаленого репозиторію:
git push --force-with-lease origin main--force-with-lease безпечніший за --force: Git перевіряє, чи не з’явилися на віддаленій гілці чужі зміни після останнього отримання даних.
Переписування історії має наслідки:
хеші комітів змінюються;
відкриті pull request можуть потребувати оновлення;
учасникам команди доведеться синхронізувати локальні копії;
старі клони можуть знову опублікувати видалену історію.
Тому очищення спільної гілки потрібно узгодити з командою. Воно також не гарантує, що секрет не встигли скопіювати або зберегти зовнішні сервіси.
Перед git push виконайте послідовність:
git status
git diff --cached
git log --oneline -5
git grep -n -I -E 'password|api[_-]?key|api[_-]?token|private[_-]?key' HEADОстання команда шукає підозрілі назви в поточному стані репозиторію. Це не повноцінний сканер секретів, але допомагає помітити очевидні помилки.
Також потрібно перевірити:
.gitignore містить локальні конфігураційні файли;
у репозиторії є лише безпечний .env.example;
у staged-змінах немає реальних значень;
секрети з CI/CD зберігаються в захищеному сховищі змінних;
опублікована історія не містить файлів або значень, які вже були видалені з поточного стану.
Для командних проєктів варто додати автоматичний сканер секретів у CI. Він має перевіряти pull request і завершувати перевірку з помилкою, якщо знаходить секрет. Локальний hook залишається корисним, але не повинен бути єдиним рівнем захисту: його можна не встановити, вимкнути або обійти.
Створіть .env.example без реальних значень.
Додайте .env та інші локальні файли до .gitignore.
Налаштуйте змінні середовища локально або в захищеному сховищі.
Перед комітом перевірте git diff --cached.
Запустіть локальний pre-commit hook або сканер.
Перед push перевірте склад коміту та його історію.
Якщо секрет витік — спочатку відкличте його, а потім очищуйте Git-історію.
.gitignore після коміту.gitignore не змінює минулі коміти. Потрібно прибрати файл з індексу, а для повного очищення — переписати історію.
Навіть після очищення Git секрет міг потрапити до журналів, кешів, fork-ів або локальних клонів. Старий ключ потрібно відкликати або замінити.
git add . без перевіркиЦя команда може додати новий конфігураційний файл або резервну копію, яку не планували публікувати. Перевіряйте результат через git status і git diff --cached.
Секрет може бути в README, тестових даних, JSON-конфігурації або звичайному вихідному коді. Перевіряйте вміст staged-змін.
Після push --force-with-lease інші учасники можуть мати несумісні локальні гілки. Узгодьте операцію та повідомте, як повторно синхронізувати репозиторії.
.env.example.env.example може бути призначений для публікації. Він повинен містити лише назви змінних, приклади безпечних значень або порожні поля.
Не зберігайте секрети у файлах, які відстежує Git.
Використовуйте змінні середовища та безпечний .env.example.
Пам’ятайте, що .gitignore не очищує вже опубліковану історію.
Перед комітом перевіряйте staged-зміни та використовуйте автоматичні перевірки.
Якщо секрет потрапив у Git, спочатку відкличте його, а потім видаліть з історії.
Переписування спільної історії потребує резервної копії, узгодження з командою та обережного git push --force-with-lease.