Пошук уроків, статей та іншого контенту
Навчіться безпечно зберігати, передавати, ротувати й відкликати паролі, токени та ключі доступу.
Секрет — це значення, яке дає доступ до даних, сервісу або можливості виконати захищену дію.
До секретів належать:
паролі користувачів і сервісних облікових записів;
API-токени;
ключі доступу до хмарних сервісів;
приватні SSH-ключі;
ключі підпису JWT або вебхуків;
рядки підключення до баз даних, якщо вони містять пароль.
Секрет відрізняється від звичайної конфігурації. Назва порту або режим роботи застосунку зазвичай не є секретом, а пароль до бази даних — є.
Основна мета керування секретами:
не зберігати їх у відкритому коді;
обмежити кількість компонентів, які мають до них доступ;
безпечно передати секрет застосунку;
регулярно замінювати секрети;
швидко відкликати скомпрометовані значення.
Секрет потрібно розглядати протягом усього його життєвого циклу:
Створення — генерується випадкове значення.
Зберігання — секрет потрапляє до захищеного сховища.
Передавання — застосунок отримує його захищеним способом.
Використання — секрет надсилається лише потрібному сервісу.
Ротація — старе значення замінюється новим.
Відкликання — старий або скомпрометований секрет перестає працювати.
Видалення — секрет і його копії прибираються зі сховищ та конфігурацій.
Успішне створення нового токена не означає, що старий токен автоматично перестав працювати. Ротація та відкликання — окремі операції, які потрібно підтримувати явно.
Не зберігайте секрети:
у файлах із вихідним кодом;
у Git-репозиторії;
у Dockerfile;
у конфігурації, яку комітять у репозиторій;
у назвах або параметрах URL;
у frontend-коді;
у повідомленнях комітів;
у скриншотах і документації;
у логах та повідомленнях про помилки.
Навіть якщо секрет пізніше видалили з файлу, він може залишитися в історії Git, кешах або резервних копіях. Якщо секрет потрапив до репозиторію, його потрібно вважати скомпрометованим і відкликати.
Для локальної розробки часто використовують файл .env, який не додають до Git:
DATABASE_URL=postgres://app_user:local_password@localhost:5432/app
PAYMENTS_API_TOKEN=local_tokenДодайте такий файл до .gitignore:
.env
.env.*
!.env.exampleФайл .env.example може містити лише назви змінних без реальних значень:
DATABASE_URL=
PAYMENTS_API_TOKEN=.env зручний для локальної роботи, але не є повноцінною системою керування секретами. У production краще використовувати спеціалізоване захищене сховище секретів або механізм секретів, який надає платформа розгортання.
У production секрет має надходити до застосунку через захищене сховище або керовану конфігурацію середовища. Важливо:
обмежити доступ до секрету конкретним сервісом;
розділити секрети для development, staging і production;
не використовувати один токен у всіх середовищах;
вести аудит доступу до сховища;
не копіювати production-секрети на ноутбуки розробників;
мати процедуру термінового відкликання.
Шифрування диска або резервної копії корисне, але саме по собі не вирішує проблему доступу. Якщо застосунок може прочитати секрет, потрібно також контролювати, хто може запускати застосунок, переглядати його пам’ять, логи та конфігурацію.
Секрет потрібно передавати:
лише через зашифрований канал;
лише компоненту, якому він необхідний;
без додавання до URL;
без запису в логи;
з мінімальним набором дозволів.
Для HTTP-запитів токен зазвичай передають у заголовку:
Authorization: Bearer <token>Не використовуйте такий підхід:
https://api.example.com/orders?token=secret-valueURL часто потрапляє до:
логів вебсерверів;
історії браузера;
систем моніторингу;
заголовка Referer;
проксі та кешів.
Зашифроване з'єднання захищає секрет під час передавання, але не захищає його від помилкового логування на стороні клієнта або сервера.
Застосунок повинен:
отримати секрет під час запуску або через контрольований механізм доступу;
перевірити, що секрет існує;
не виводити його значення;
використовувати його лише для потрібної операції;
не повертати його у відповіді клієнту;
не включати його до повідомлень про помилки.
Приклад для Node.js:
const apiUrl = process.env.PAYMENTS_API_URL;
const apiToken = process.env.PAYMENTS_API_TOKEN;
if (!apiUrl) {
throw new Error("Не задано PAYMENTS_API_URL");
}
if (!apiToken) {
throw new Error("Не задано PAYMENTS_API_TOKEN");
}
let paymentsUrl;
try {
paymentsUrl = new URL("/health", apiUrl);
} catch {
throw new Error("PAYMENTS_API_URL має бути коректним URL");
}
async function checkPaymentsService() {
const response = await fetch(paymentsUrl, {
headers: {
Authorization: `Bearer ${apiToken}`,
Accept: "application/json"
}
});
if (response.status === 401 || response.status === 403) {
throw new Error("Сервіс платежів відхилив облікові дані");
}
if (!response.ok) {
throw new Error(`Сервіс платежів повернув HTTP ${response.status}`);
}
return response.json();
}
checkPaymentsService()
.then(() => {
console.log("Сервіс платежів доступний");
})
.catch((error) => {
// Логуємо лише безпечне повідомлення, не значення токена
console.error(error.message);
process.exitCode = 1;
});Запуск із секретом через змінні середовища:
PAYMENTS_API_URL=https://payments.internal.example \
PAYMENTS_API_TOKEN='token-value' \
node app.jsУ production ці змінні має встановлювати платформа або система керування секретами, а не користувач, який вручну запускає команду.
Краще завершити запуск, якщо обов’язковий секрет відсутній. Небезпечно непомітно перейти на порожнє значення, тестовий токен або іншу конфігурацію.
Не робіть так:
const token = process.env.API_TOKEN || "default-token";Якщо змінна не задана, застосунок повинен повідомити про помилку, а не використовувати вбудований секрет.
Паролі користувачів не потрібно зберігати як секрети, які можна розшифрувати. Сервер має зберігати хеш пароля разом із випадковою сіллю.
Під час входу:
користувач надсилає пароль через захищене з'єднання;
сервер обчислює хеш отриманого пароля;
результат порівнюється зі збереженим хешем;
сам пароль не записується в базу даних і логи.
Для хешування паролів використовуйте спеціально призначені алгоритми, наприклад Argon2id, bcrypt або scrypt, через перевірену бібліотеку. Не використовуйте звичайний SHA-256 без спеціального налаштування: швидкі хеші легше перебирати.
Це відрізняється від API-токена. Токен сервісу часто потрібно передати іншому сервісу в початковому вигляді, тому його зберігають у захищеному сховищі та можуть відкликати.
Кожен секрет повинен мати лише ті дозволи, які необхідні його власнику.
Наприклад:
сервіс читання звітів не повинен мати права видаляти дані;
frontend не повинен отримувати приватний ключ сервера;
окремий сервіс повинен мати окремий токен;
токени development не повинні мати доступу до production;
доступ до бази даних краще обмежити потрібною базою та операціями.
Якщо один токен використовується всіма сервісами, його компрометація створює більшу область ураження. Окремі токени спрощують аудит, ротацію та відкликання.
Ротація — це планова заміна секрету новим.
Причини для ротації:
завершення терміну дії;
зміна працівника або команди;
зміна політики безпеки;
підозра на витік;
вимоги аудиту;
зменшення часу дії токенів.
Безпечна ротація для сервісного токена зазвичай має такий вигляд:
створити новий токен із тими самими мінімальними дозволами;
додати новий токен до сховища секретів;
оновити застосунок або виконати його перезапуск;
перевірити, що застосунок працює з новим токеном;
відкликати старий токен;
перевірити логи та метрики після зміни.
Якщо система дозволяє лише один активний токен, ротація може спричинити короткочасний простій. Якщо можна мати два активні токени, спочатку вмикають новий, а старий відкликають після перевірки. Такий підхід називають ротацією з перекриттям.
Ротація повинна бути автоматизованою або принаймні задокументованою. Рідкісна ручна процедура часто не працює під час інциденту.
Відкликання — це негайне припинення дії секрету.
Відкликайте секрет, якщо:
він потрапив у репозиторій;
він з'явився в логах;
його надіслали не тому адресату;
втрачено пристрій або ключ;
є підозра на несанкціонований доступ;
працівник або сервіс більше не повинні мати доступу.
Після витоку не достатньо просто видалити секрет із файлу. Потрібно:
відкликати старе значення у сервісі-власнику;
створити нове значення;
оновити всі компоненти, які його використовують;
перевірити журнали доступу;
з'ясувати, де ще могли залишитися копії;
зафіксувати інцидент і його наслідки.
Не покладайтеся лише на приховування значення в інтерфейсі. Якщо токен залишився дійсним, його можна використовувати незалежно від того, чи видно його в системі.
Логи повинні допомагати знаходити проблеми, але не створювати нові витоки.
Не записуйте:
пароль;
повний токен;
приватний ключ;
повний рядок підключення;
заголовок Authorization;
тіло запиту, якщо воно може містити секрет.
Безпечніше логувати:
ідентифікатор операції;
назву сервісу;
HTTP-статус;
час виконання;
ідентифікатор секрету без його значення;
факт помилки автентифікації.
Якщо потрібно відрізнити один токен від іншого, використовуйте окремий публічний ідентифікатор або версію секрету, а не частину самого значення. Навіть маскування на кшталт abcd...1234 не завжди безпечне: частина секрету може допомогти його ідентифікувати або підібрати.
Для початку можна дотримуватися такого процесу:
Визначити всі секрети проєкту.
Зберігати локальні значення у .env, доданому до .gitignore.
Додати .env.example без реальних значень.
У production передавати секрети через захищене сховище платформи.
Не друкувати секрети в логах.
Створювати окремі значення для кожного середовища.
Використовувати мінімальні дозволи.
Документувати, як виконати ротацію та відкликання.
Перевіряти репозиторій і журнали після підозри на витік.
Негайно відкликати секрет, якщо він став публічним.
const token = "real-production-token";Цей токен потрапить до Git, код-рев’ю, резервних копій і, можливо, до зібраного артефакту. Використовуйте змінну середовища або захищене сховище.
.env додано до Git.env може виглядати як локальна конфігурація, але часто містить справжні production або тестові секрети. Перевіряйте .gitignore до першого коміту.
URL часто записується в логах і проксі. Для токенів використовуйте захищені заголовки або механізм автентифікації, передбачений конкретним сервісом.
Виведення всього об’єкта HTTP-запиту може включити заголовки з токеном. Перевіряйте, що саме логерує бібліотека або middleware.
Компрометація тестового сервера тоді може відкрити доступ до production. Створюйте окремі секрети для кожного середовища.
Токен без терміну дії може залишатися корисним після витоку протягом років. Використовуйте термін дії, ротацію та відкликання, якщо це підтримує сервіс.
У результаті активними залишаються обидва значення. Після перевірки нового токена старий потрібно відкликати або встановити для нього термін завершення дії.
Код на кшталт process.env.TOKEN || "temporary-token" маскує помилку конфігурації. Краще зупинити застосунок і виправити конфігурацію.
Секрети — це паролі, токени, приватні ключі та інші значення, що надають доступ.
Не зберігайте секрети у вихідному коді, Git, URL, frontend-коді або логах.
Для локальної розробки використовуйте .env, який не комітиться.
У production передавайте секрети через захищене сховище або механізм платформи.
Паролі користувачів зберігайте як стійкі хеші, а не у відкритому вигляді.
Використовуйте мінімальні дозволи й окремі секрети для різних середовищ та сервісів.
Ротація замінює секрет планово, а відкликання негайно припиняє його дію.
Якщо секрет витік, його потрібно відкликати, створити новий і перевірити можливі копії.