Asosiy kontentga o‘tish

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.

security django backend

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#

Terminal
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:

Terminal
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:

Terminal
# .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.

Terminal
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:

Terminal
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#

Python
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:

Natija
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:

Fayl tuzilishi
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:

Natija
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.

Python
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.

6 daqiqa 563 marta o‘qildi