Пошук уроків, статей та іншого контенту
Безпечно керуватимемо ключами, паролями та токенами через змінні середовища без зберігання секретів у коді.
Змінна середовища — це значення, яке програма отримує з оточення операційної системи під час запуску.
У Node.js змінні середовища доступні через об’єкт process.env:
console.log(process.env.NODE_ENV);
console.log(process.env.PORT);Значення у process.env завжди мають тип string або undefined, якщо змінну не задано.
Змінні середовища зручно використовувати для:
ключів доступу до API;
паролів до бази даних;
токенів авторизації;
адрес зовнішніх сервісів;
номера порту;
налаштувань, які відрізняються у розробці та на сервері.
Головна ідея: секретні значення не повинні бути записані безпосередньо у вихідному коді.
Змінну можна задати під час запуску програми.
У Linux або macOS:
APP_NAME=demo PORT=3000 node app.jsУ Windows PowerShell:
$env:APP_NAME="demo"
$env:PORT="3000"
node app.jsФайл app.js:
const appName = process.env.APP_NAME;
const port = Number(process.env.PORT || 3000);
console.log(`Назва застосунку: ${appName}`);
console.log(`Порт: ${port}`);Якщо APP_NAME не задана, її значенням буде undefined.
Для необов’язкових змінних можна використовувати значення за замовчуванням:
const port = Number(process.env.PORT || 3000);У цьому прикладі програма використовує порт 3000, якщо змінна PORT не задана.
Секрети зазвичай є обов’язковими. Якщо ключ не задано, краще завершити програму з чіткою помилкою, а не продовжувати роботу з неправильними налаштуваннями.
const apiKey = process.env.API_KEY;
if (!apiKey) {
console.error('Помилка: змінна API_KEY не задана');
process.exit(1);
}
console.log('API-ключ успішно завантажено');Програма не виводить сам ключ у консоль. Навіть під час розробки не варто без потреби показувати секрети в логах.
.envПід час локальної розробки незручно щоразу задавати змінні в командному рядку. Для цього часто використовують файл .env.
Приклад .env:
API_KEY=local-api-key
DATABASE_PASSWORD=local-password
PORT=3000Файл .env містить звичайний текст, тому він не шифрує секрети. Його перевага в іншому: секрети можна відокремити від коду та не додавати до системи контролю версій.
.env за допомогою dotenvВстановіть пакет:
npm install dotenvНа початку файлу застосунку викличте config():
require('dotenv').config();
const apiKey = process.env.API_KEY;
const port = Number(process.env.PORT || 3000);
if (!apiKey) {
console.error('Помилка: змінна API_KEY не задана');
process.exit(1);
}
console.log(`Сервер буде запущено на порту ${port}`);
console.log('API-ключ завантажено');Після цього запустіть програму:
node app.jsdotenv прочитає значення з .env і додасть їх до process.env.
Викликайте
require('dotenv').config()до того, як код почне читати змінні середовища.
Створімо невеликий застосунок, якому потрібні порт і секретний токен.
Файл .env:
PORT=4000
SERVICE_TOKEN=example-local-tokenФайл app.js:
require('dotenv').config();
const portValue = process.env.PORT || '3000';
const serviceToken = process.env.SERVICE_TOKEN;
const port = Number(portValue);
if (!Number.isInteger(port) || port < 1 || port > 65535) {
console.error('Помилка: PORT має бути цілим числом від 1 до 65535');
process.exit(1);
}
if (!serviceToken) {
console.error('Помилка: змінна SERVICE_TOKEN не задана');
process.exit(1);
}
console.log(`Застосунок налаштовано на порт ${port}`);
console.log('Секретний токен завантажено, але не виводиться');Встановлення залежності та запуск:
npm install dotenv
node app.jsОчікуваний результат:
Застосунок налаштовано на порт 4000
Секретний токен завантажено, але не виводитьсяТокен не показується в результаті роботи програми.
Файл .env потрібно додати до .gitignore.
Файл .gitignore:
node_modules/
.envПісля цього Git не відстежуватиме .env.
Зазвичай до репозиторію додають приклад без реальних секретів, наприклад .env.example:
PORT=3000
SERVICE_TOKEN=вкажіть-токен-локальноТакий файл пояснює, які змінні потрібні застосунку, але не містить справжніх ключів або паролів.
Різниця між файлами:
.env — локальні значення, зокрема секрети; не додаємо до Git;
.env.example — перелік необхідних змінних із безпечними прикладами; можна додати до Git.
На сервері секрети зазвичай задаються не через файл у репозиторії, а в налаштуваннях середовища запуску:
API_KEY=production-key node app.jsПрограма читає значення так само:
const apiKey = process.env.API_KEY;Код не має знати, звідки саме надійшло значення. Для нього джерело однакове — process.env.
Не слід вбудовувати секрети в:
JavaScript-файли;
конфігурацію, яку зберігають у Git;
повідомлення про помилки;
логи;
приклади коду з реальними ключами.
Для змінних середовища часто використовують великі літери та підкреслення:
DATABASE_URL=postgres://localhost:5432/app
API_TIMEOUT=5000
NODE_ENV=developmentПроте всі значення читаються як рядки:
const timeout = process.env.API_TIMEOUT;
console.log(typeof timeout); // stringЯкщо потрібне число, його треба перетворити явно:
const timeout = Number(process.env.API_TIMEOUT || 5000);Будьте уважні до булевих значень:
DEBUG=falseЗначення process.env.DEBUG буде рядком "false". Такий код працює неправильно:
if (process.env.DEBUG) {
console.log('Режим налагодження увімкнено');
}Рядок "false" є непорожнім, тому JavaScript вважає його істинним. Для перевірки потрібно порівняти значення:
const debug = process.env.DEBUG === 'true';
if (debug) {
console.log('Режим налагодження увімкнено');
}Небезпечно:
const apiKey = 'real-secret-key';Безпечніше:
const apiKey = process.env.API_KEY;.env до репозиторіюЯкщо .env уже був доданий до Git, додавання його до .gitignore саме по собі не видалить файл із попередніх комітів. Потрібно припинити його відстеження та, якщо секрет уже потрапив у репозиторій, замінити цей секрет.
Особливо важливо змінити ключ або пароль, який став доступним іншим людям.
Небезпечно:
console.log('API_KEY:', process.env.API_KEY);Логи можуть зберігатися на сервері або бути доступними іншим користувачам. Виводьте лише факт наявності налаштування:
console.log(process.env.API_KEY ? 'API-ключ задано' : 'API-ключ відсутній');config()Якщо використовується пакет dotenv, але config() не викликано, значення з .env не завантажаться:
require('dotenv').config();Цей рядок має виконуватися до читання process.env.
Значення PORT, TIMEOUT та подібні потрібно перетворювати на числа й перевіряти. Не передавайте неперевірені значення далі в програму.
Змінні середовища доступні в Node.js через process.env.
Секрети, паролі, ключі та токени не потрібно зберігати у вихідному коді.
Локально змінні можна зберігати у .env і завантажувати за допомогою dotenv.
.env потрібно додати до .gitignore.
Для репозиторію можна створити .env.example без справжніх секретів.
Значення з process.env завжди є рядками.
Обов’язкові змінні потрібно перевіряти під час запуску.
Секрети не можна виводити в логи або повідомлення про помилки.