Node.jsМодуль 6: Безопасность
Переменные окружения
dotenv, конфигурация, секреты
Цель урока
В этом уроке ты научишься:
- Использовать переменные окружения
- Настраивать dotenv
- Управлять конфигурацией
Зачем нужны
- Хранение секретов (API ключи, пароли)
- Разные настройки для dev/production
- Не коммитить чувствительные данные
Никогда не коммить секреты!
Добавь .env в .gitignore.
dotenv
Установка
npm install dotenvИспользование
// В начале приложения
require('dotenv').config();
// Теперь доступны
console.log(process.env.DATABASE_URL);
console.log(process.env.JWT_SECRET);.env файл
# .env
NODE_ENV=development
PORT=3000
# Database
DATABASE_URL=mongodb://localhost:27017/myapp
# JWT
JWT_SECRET=your-super-secret-key
JWT_EXPIRES_IN=7d
# OAuth
GOOGLE_CLIENT_ID=xxx
GOOGLE_CLIENT_SECRET=xxx
# External APIs
API_KEY=xxxРазные окружения
Структура файлов
.env # Общие настройки (не коммитить)
.env.example # Пример (коммитить)
.env.development # Для разработки
.env.production # Для production
.env.test # Для тестов.env.example
# .env.example (коммитить в git)
NODE_ENV=development
PORT=3000
DATABASE_URL=
JWT_SECRET=Загрузка по окружению
const dotenv = require('dotenv');
const envFile = process.env.NODE_ENV === 'production'
? '.env.production'
: '.env.development';
dotenv.config({ path: envFile });Конфигурационный модуль
// config/index.js
require('dotenv').config();
const config = {
env: process.env.NODE_ENV || 'development',
port: parseInt(process.env.PORT, 10) || 3000,
db: {
url: process.env.DATABASE_URL,
options: {
useNewUrlParser: true,
useUnifiedTopology: true
}
},
jwt: {
secret: process.env.JWT_SECRET,
expiresIn: process.env.JWT_EXPIRES_IN || '7d'
},
cors: {
origin: process.env.CORS_ORIGIN?.split(',') || ['http://localhost:3000']
},
isProduction: process.env.NODE_ENV === 'production',
isDevelopment: process.env.NODE_ENV === 'development'
};
// Валидация обязательных переменных
const requiredEnvVars = ['DATABASE_URL', 'JWT_SECRET'];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
throw new Error(`Missing required env variable: ${envVar}`);
}
}
module.exports = config;Использование
const config = require('./config');
mongoose.connect(config.db.url, config.db.options);
app.listen(config.port, () => {
console.log(`Server running on port ${config.port}`);
});Валидация с Joi
const Joi = require('joi');
const envSchema = Joi.object({
NODE_ENV: Joi.string()
.valid('development', 'production', 'test')
.default('development'),
PORT: Joi.number().default(3000),
DATABASE_URL: Joi.string().required(),
JWT_SECRET: Joi.string().min(32).required(),
JWT_EXPIRES_IN: Joi.string().default('7d')
}).unknown();
const { error, value } = envSchema.validate(process.env);
if (error) {
throw new Error(`Config validation error: ${error.message}`);
}
module.exports = {
env: value.NODE_ENV,
port: value.PORT,
db: { url: value.DATABASE_URL },
jwt: { secret: value.JWT_SECRET, expiresIn: value.JWT_EXPIRES_IN }
};Секреты в production
Не используй .env в production
Вместо этого:
- Переменные окружения сервера
- Secret managers (AWS Secrets Manager, HashiCorp Vault)
- Kubernetes Secrets
- Docker secrets
Пример Docker
# Dockerfile
ENV NODE_ENV=production
# Не копируй .env в образ!# docker-compose.yml
services:
app:
environment:
- DATABASE_URL=${DATABASE_URL}
- JWT_SECRET=${JWT_SECRET}Практика
Задание: Конфигурация
Задача: Создай модуль конфигурации с валидацией.
Решение:
// config.js
require('dotenv').config();
const required = ['DATABASE_URL', 'JWT_SECRET'];
for (const key of required) {
if (!process.env[key]) {
throw new Error(`Missing env: ${key}`);
}
}
module.exports = {
port: process.env.PORT || 3000,
db: process.env.DATABASE_URL,
jwt: {
secret: process.env.JWT_SECRET,
expiresIn: process.env.JWT_EXPIRES_IN || '7d'
},
isProd: process.env.NODE_ENV === 'production'
};Проверь себя
- Почему нельзя коммитить .env?
- Как загрузить разные .env для разных окружений?
- Как хранить секреты в production?