Environment Variables — dotenv, maxfiy ma'lumotlarni saqlash

~10 daq · Express · Autentifikatsiya va Xavfsizlik

Muammodan boshlaylik

Loyiha tayyor. GitHub ga push qilasiz. Bir hafta o'tdi — kimdir sizning repository ni topdi va ichida bunday ko'rdi:

const jwt = require('jsonwebtoken');

const token = jwt.sign({ userId: user.id }, 'mening_super_secret_kalitim_12345');

const db = new Pool({
  host: 'localhost',
  password: 'admin123',   // ← ma'lumotlar bazasi paroli
  database: 'myapp'
});

Endi sizning JWT secret va database parolingiz butun internet ko'rishi mumkin. Bu credential leak — eng keng tarqalgan xavfsizlik muammolaridan biri.

2022 yilda Toyota-ning GitHub repository si ommaviy bo'lib ketdi — ichida AWS kalitlari bor edi. 296,000 mijozning ma'lumotlari xavf ostida qoldi.


Environment Variables nima?

Environment variables — dastur tashqarisida, operatsion tizim darajasida saqlanadigan o'zgaruvchilar. Kod ichida emas.

Hayotiy o'xshatma

Tasavvur qiling: oshpaz retseptni yozadi — barcha ko'rsatmalar batafsil, lekin "tuzni qo'shing" deyilgan joyda miqdor yozilmagan. Negaki har xil restoran o'z tuz miqdorini o'zi belgilaydi. Retsept (kod) umumiy, sozlamalar (environment) har joyda o'ziga xos.

Retsept (kod) = GitHub da ochiq
Tuz miqdori (secret) = faqat oshpaz biladi

dotenv nima va qanday ishlaydi?

dotenv — .env faylidan o'zgaruvchilarni o'qib, process.env ga yuklaydi.

npm install dotenv

.env fayli

# .env
PORT=3000
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DB_USER=postgres
DB_PASSWORD=super_secret_password

JWT_SECRET=bu_hech_kim_bilmasligi_kerak_uzun_random_kalit
JWT_EXPIRES_IN=7d

NODE_ENV=development

Asosiy ishlatish

// app.js — eng birinchi qatorda bo'lishi kerak
require('dotenv').config(); // ← .env ni o'qiydi va process.env ga yuklaydi

const express = require('express');
const { Pool } = require('pg');
const jwt = require('jsonwebtoken');

const app = express();

// ✅ Endi maxfiy ma'lumotlar kodda yo'q
const pool = new Pool({
  host: process.env.DB_HOST,       // ← .env dan olinadi
  port: process.env.DB_PORT,
  database: process.env.DB_NAME,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
});

app.post('/login', async (req, res) => {
  // ...
  const token = jwt.sign(
    { userId: user.id },
    process.env.JWT_SECRET,        // ← .env dan olinadi
    { expiresIn: process.env.JWT_EXPIRES_IN }
  );
  res.json({ token });
});

const PORT = process.env.PORT || 3000; // ← default qiymat bilan
app.listen(PORT, () => console.log(`Server ${PORT} portda ishlamoqda`));

.gitignore — eng muhim qadam

.env faylini hech qachon Git ga qo'shmang:

# .gitignore
.env
.env.local
.env.production
node_modules/

Nima uchun .env.local va .env.production ham? Chunki ba'zi loyihalarda bir nechta .env fayl bo'ladi — hammasini gitignore ga qo'shing.

.env.example — jamoa uchun shablon

.env ni Git ga qo'shmaysiz, lekin jamoadoshlar qanday o'zgaruvchilar kerakligini bilishi kerak. Buning uchun .env.example yaratinglar:

# .env.example — bu fayl Git da saqlanadi (qiymatlar bo'sh)
PORT=
DB_HOST=
DB_PORT=
DB_NAME=
DB_USER=
DB_PASSWORD=

JWT_SECRET=
JWT_EXPIRES_IN=

NODE_ENV=

Yangi dasturchi loyihani clone qilganda:

cp .env.example .env
# Keyin .env ni o'z qiymatlari bilan to'ldiradi

Turli muhitlar uchun turli .env

.env                  ← lokal ishlab chiqish
.env.test             ← testlar uchun
.env.production       ← production (server da to'g'ridan-to'g'RI o'rnatiladi)
// Test muhitida boshqa .env yuklash
require('dotenv').config({ path: '.env.test' });

Production da .env fayl ishlatilmaydi — o'zgaruvchilar to'g'ridan-to'g'RI server muhitiga o'rnatiladi:

# Linux/Mac server da
export JWT_SECRET=production_super_secret_kalit
export DB_PASSWORD=production_db_paroli

# Yoki PM2 ecosystem.config.js orqali
# Yoki Docker --env-file orqali
# Yoki Heroku/Vercel dashboard orqali

Qiymatlarni tekshirish

Muhim o'zgaruvchilar yuklanmaganida dastur noto'g'ri ishlashi mumkin. Boshlashdan oldin tekshirib oling:

// config/env.js
require('dotenv').config();

const requiredEnvVars = [
  'DB_HOST',
  'DB_PASSWORD',
  'JWT_SECRET',
];

requiredEnvVars.forEach((varName) => {
  if (!process.env[varName]) {
    console.error(`Xato: ${varName} o'zgaruvchisi topilmadi!`);
    process.exit(1); // ← noto'g'ri konfiguratsiya bilan ishlamaslik
  }
});

module.exports = {
  port: parseInt(process.env.PORT) || 3000,
  db: {
    host: process.env.DB_HOST,
    port: parseInt(process.env.DB_PORT) || 5432,
    name: process.env.DB_NAME,
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
  },
  jwt: {
    secret: process.env.JWT_SECRET,
    expiresIn: process.env.JWT_EXPIRES_IN || '7d',
  },
  nodeEnv: process.env.NODE_ENV || 'development',
};
// app.js
const config = require('./config/env'); // ← bir joydan olasiz

const pool = new Pool(config.db);       // ← config.db.host, config.db.password...
app.listen(config.port);

Nima uchun config obyektiga o'rash? process.env.DB_HOST ni har joyda yozish o'rniga bir martalik tekshiruv va markazlashgan konfiguratsiya. Bir joyda o'zgartirasiz — hamma joyda o'zgaradi.


Tez-tez uchraydigan xatolar

1. dotenv ni kech yuklash

// ❌ — db.js da config ishlatilishidan KEYIN yuklangan
const db = require('./db');     // ← process.env.DB_HOST undefined bo'ladi
require('dotenv').config();     // ← bu kech
// ✅ — app.js ning eng boshida
require('dotenv').config();     // ← birinchi bo'lishi shart
const db = require('./db');

Sababi: require chaqirilganda modul darhol ishga tushadi. db.js yuklanganda process.env.DB_HOST hali bo'sh.

2. .env faylini Git ga push qilish

# ❌ — bu xato
git add .env
git commit -m "sozlamalar qo'shildi"
git push

Agar tasodifan push qilsangiz — darhol parollarni almashtiring. Git tarixidan o'chirish murakkab va ishonchsiz.

3. process.env qiymatlarini tip tekshirmasdan ishlatish

// ❌ — PORT string bo'lib keladi, raqam emas
app.listen(process.env.PORT); // ko'pincha ishlaydi, lekin...

const maxRetries = process.env.MAX_RETRIES;
for (let i = 0; i < maxRetries; i++) { ... } // ← maxRetries "5" (string), taqqoslash xato
// ✅ — parseInt bilan son ga o'tkazish
app.listen(parseInt(process.env.PORT) || 3000);
const maxRetries = parseInt(process.env.MAX_RETRIES) || 3;

Xulosa

  • .env — maxfiy ma'lumotlarni kod tashqarisida saqlash usuli
  • .gitignore ga .env qo'shing — bu majburiy
  • .env.example — qiymatlarssiz shablon fayl, Git da saqlanadi
  • Production da .env fayl emas, server muhit o'zgaruvchilari ishlatiladi
  • process.env dan olgan qiymatlar har doim string — raqam kerak bo'lsa parseInt ishlating

Keyingi dars: 32-dars: Multer — fayl yuklash, storage, filter, size limit