Maqolalar 6 daqiqa 563 ko‘rish
Django sozlamalarini .env bilan boshqarish
settings.py ichida parol saqlash — klassik xato, chunki u kod bilan birga git'ga, git bilan birga GitHub'ga tushadi. Sozlamalarni muhitga chiqaramiz, settings'ni dev va prod'ga bo‘lamiz va nega bu xavfsizlik masalasi ekanini ko‘ramiz.
Mundarija
Django loyihalarida eng ko‘p uchraydigan xatolardan biri ko‘pincha eng
begunoh ko‘rinishga ega. settings.py faylini ochasiz, u yerda SECRET_KEY,
baza paroli, biror API kaliti bemalol yozib qo‘yilgan. Hozircha ishlayapti,
demak muammo yo‘qdek. Lekin settings.py — bu kod, kod esa git'ga tushadi,
git ertami-kechmi GitHub'ga chiqadi. Va o‘sha paytdan boshlab parolingiz
loyiha tarixida abadiy qoladi, uni keyingi commit bilan o‘chirib ham
bo‘lmaydi.
To‘g‘ri yondashuv bitta jumlaga sig‘adi: konfiguratsiya kodda emas, muhitda
yashashi kerak. Bu yangi ixtiro emas, mashhur "o‘n ikki omil" tamoyillarining
uchinchisi. Django'da buni django-environ degan kutubxona bilan qulay
qilamiz.
O‘rnatish va .env fayl#
uv add django-environ
Loyiha ildizida .env fayl ochamiz. Lekin birinchi qadam fayl yaratish emas —
uni git'dan yashirish. Shuning uchun avval .gitignore ga qator qo‘shamiz,
keyin faylni yaratamiz:
echo ".env" >> .gitignore
Tartib shunday bo‘lgani muhim: agar .env ni avval yaratib, keyin
.gitignore ni yozsangiz, oradagi bitta git add . butun mashaqqatni
behuda qiladi. Endi faylning o‘zi:
# .env — bu fayl git'ga TUSHMAYDI
SECRET_KEY=juda-maxfiy-kalit
DATABASE_URL=postgres://app:app@localhost:5432/app
ALLOWED_HOSTS=example.uz,www.example.uz
.env fayli oddiy ko‘rinadi, lekin qoidasi qat'iy#
Bu fayl YAML ham, Python ham emas — uning o‘z, juda sodda qoidalari bor va ikkitasida odam jimgina qoqiladi. Ikkalasini ham sinab ko‘rdim.
Teng belgisi atrofida probel qo‘ymang.
BOSHQA = probel bilan
Bunday qator o‘qilmaydi. Ekranda Invalid line: BOSHQA = probel bilan degan
kichik ogohlantirish chiqadi, uni sezmasligingiz oson, keyin esa
o‘zgaruvchini so‘raganingizda ImproperlyConfigured olasiz.
Qiymat yonida izoh yozmang. # belgisi qator boshida izoh, qiymat
yonida esa shunchaki belgi:
IZOHLI=qiymat # bu izohmi?
Bu qatorning qiymati qiymat emas, 'qiymat # bu izohmi?' bo‘lib chiqadi —
izoh bilan, probellari bilan birga. Parol yoki token yonida shunday yozsangiz,
sir jimgina buzilgan holda ishlatiladi va xatoni topish soatlab vaqt oladi.
Izohni doim alohida qatorga chiqaring.
Jamoa uchun esa yoniga .env.example faylini qo‘yamiz — bu qiymatlarsiz,
faqat o‘zgaruvchi nomlari turadigan shablon, va u aksincha git'da qoladi.
Shunda yangi kelgan odam qaysi o‘zgaruvchilar kerakligini kod ichini
titkilamasdan biladi.
settings.py'da o‘qish#
import environ
env = environ.Env()
environ.Env.read_env(BASE_DIR / ".env")
SECRET_KEY = env("SECRET_KEY")
DEBUG = env.bool("DEBUG", default=False)
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=[])
DATABASES = {"default": env.db("DATABASE_URL")}
Bir nozik joyga e'tibor bering: SECRET_KEY da default qiymat yo‘q. Bu
ataylab. Agar kalit topilmasa, loyiha ishga tushishdayoq baland ovozda
yiqiladi — aynan shunday:
django.core.exceptions.ImproperlyConfigured: Set the SECRET_KEY environment variable
Alternativasi ancha yomon: jimgina biror bo‘sh yoki soxta qiymat bilan ishlab ketib, keyin allaqachon production'da g‘alati xato berishi. Majburiy sir uchun jimgina default qo‘yish — bu kelajakdagi tushunarsiz nosozlikning urug‘i.
Aytgancha, read_env faylni topa olmasa hech qanday xato bermaydi, jimgina
davom etadi. Ya'ni .env ni yaratishni butunlay unutsangiz ham, sizni
ogohlantiradigan yagona narsa — aynan yuqoridagi ImproperlyConfigured
bo‘ladi. Bu ham defaultsiz qoldirish foydasiga yana bir dalil.
Settings'ni dev va prod'ga bo‘lish#
Loyiha o‘sgani sari bitta settings.py yetmay qoladi, chunki lokal
kompyuterdagi va serverdagi sozlamalar bir xil bo‘la olmaydi. Shuning uchun
uni uchta faylga bo‘lamiz:
config/settings/
├── base.py # ikkalasiga umumiy sozlamalar
├── dev.py # DEBUG=True va lokal qulayliklar
└── prod.py # xavfsizlik: HSTS, secure cookie, SSL redirect
dev.py va prod.py ikkalasi ham from .base import * bilan boshlanadi va
faqat o‘z farqini yozadi. Qaysi biri ishlashini esa DJANGO_SETTINGS_MODULE
degan muhit o‘zgaruvchisi hal qiladi: lokal kompyuterda u config.settings.dev
ga, serverda config.settings.prod ga ishora qiladi.
Bu bo‘linish sizga hoziroq kerakmi
Agar hozir muhit bitta bo‘lsa — faqat sizning kompyuteringiz — bitta
settings.py va .env dan olinadigan DEBUG yetib turadi. Fayllarni
uchga bo‘lish o‘zi bilan yangi savollar olib keladi
(DJANGO_SETTINGS_MODULE qayerda o‘rnatiladi, import * nega kerak), bu
esa asosiy mavzuni o‘rganayotganda chalg‘itadi.
Bo‘linish ikkinchi muhit paydo bo‘lgandan keyin o‘zini oqlaydi: server qo‘shilganda, keyinroq staging qo‘shilganda. Ya‘ni yuqoridagi tuzilma noto‘g‘ri emas — shunchaki hammaga birinchi kundan kerak emas.
Nega bularning hammasi xavfsizlik masalasi#
Uch narsa birdan yaxshilanadi. Birinchidan, .env git'da bo‘lmagani uchun
parollar loyiha tarixiga umuman tushmaydi. Ikkinchidan, production'ning
xavfsizlik sozlamalari — HSTS, xavfsiz cookie, HTTPS'ga yo‘naltirish —
alohida faylda yashaydi, shuning uchun ularni "lokalda qulay bo‘lsin" deb
vaqtincha o‘chirish vasvasasi yo‘qoladi. Uchinchidan, kalitni almashtirish
uchun endi qayta deploy shart emas: serverdagi .env yangilanadi va xizmat
qayta ishga tushiriladi.
Kalit allaqachon git'ga tushib bo‘lgan bo‘lsa#
Maqolaning boshida "keyingi commit bilan o‘chirib bo‘lmaydi" degan edim — endi nima qilish kerakligini ham aytay, chunki bu holat juda ko‘p uchraydi.
Birinchi va eng muhim qadam: kalitni almashtiring. Faylni git'dan o‘chirish ham, tarixni qayta yozish ham ikkinchi darajali ish, chunki o‘sha kalit allaqachon boshqa odamning kompyuteridagi klonda, CI keshida yoki GitHub'ning o‘z keshida bo‘lishi mumkin. Sizib chiqqan sir — bu endi sir emas, uni yashirish emas, bekor qilish kerak.
Django'da SECRET_KEY ni almashtirishning bitta sezilarli oqibati bor:
u bilan imzolangan hamma narsa yaroqsiz bo‘lib qoladi. Sinab ko‘rdim —
eski kalit bilan imzolangan qiymatni yangisi bilan o‘qimoqchi bo‘lsangiz:
BadSignature: Signature "hHmw18RCZFxCyF_4..." does not match
Amalda bu degani: hamma foydalanuvchi tizimdan chiqib ketadi, yuborilgan
parol tiklash havolalari ishlamay qoladi. Kichik loyihada bu muammo emas,
kattasida esa — muammo. Django buning uchun SECRET_KEY_FALLBACKS degan
sozlamani beradi: yangi kalitni asosiy qilib qo‘yasiz, eskisini esa
vaqtincha ro‘yxatda qoldirasiz.
SECRET_KEY = env("SECRET_KEY")
SECRET_KEY_FALLBACKS = env.list("SECRET_KEY_FALLBACKS", default=[])
Shunda yangi signature'lar yangi kalit bilan qo‘yiladi, eski signature'lar esa hamon o‘qiladi — buni ham tekshirib ko‘rdim, ishlaydi. Bir necha haftadan keyin eski kalitni ro‘yxatdan olib tashlaysiz va o‘tish tugaydi.
Oddiy va zerikarli ko‘ringan bu tartib aslida loyiha o‘sganda eng arzon sug‘urtaga aylanadi — bir marta to‘g‘ri qo‘ysangiz, keyin u haqda umuman o‘ylamaysiz.