IlmHamroh
JavaScript Full-stack/28-qism. Autentifikatsiya va backend xavfsizligi2/31-dars18 daqiqa
Mundarija (27)

Xavfsizlik tamoyillari: chuqur himoya, eng kam huquq va sukut bo'yicha xavfsizlik

Qisqacha: Har bir zaiflikni alohida eslab qolish shart emas — bir nechta umumiy qoida ko'p hujumni oldindan to'xtatadi. Chuqur himoya — bitta qulfga emas, bir necha qatlamga tayaning. Eng kam huquq — har dastur va odamga faqat kerakli ruxsat. Sukut bo'yicha xavfsiz — sozlanmagan narsa yopiq. Xavfsiz yiqilish — xato bo'lsa, eshik yopiladi. Hech kimga ishonma — har so'rov tekshiriladi. Ruxsat ro'yxati (allowlist) — "nima mumkin"ni sanang, "nima mumkin emas"ni emas.

Bu darsda

  • Yettita asosiy xavfsizlik tamoyilini o'z so'zingiz bilan tushuntirasiz.
  • Har tamoyilni vazifalar-api dan haqiqiy misol bilan ko'rsatasiz.
  • O'z Postgres bazangizda RLS qatlami superuser bilan ishlamasligini va oddiy rol bilan ishlashini tekshirasiz.
  • "Ochiq yiqilish" xatosini kodda topib, tuzatasiz.
  • Ruxsat ro'yxati va taqiq ro'yxati farqini Object.hasOwn misolida ko'rasiz.

Oldin bilishingiz kerak: Xavfsizlik nima: CIA, tahdid va hujumchi fikrlashi, Row Level Security, Rollar va huquqlar, Env validatsiyasi va tipli config obyekti.

Oldingi darsda vazifalar-api ga hujumchi ko'zi bilan qaradik va besh xil teshik topdik. Ularni birma-bir yamash mumkin. Lekin ertaga yangi kod yoziladi va yangi teshik paydo bo'ladi. Bugun teshiklar paydo bo'lishining o'zini kamaytiradigan qoidalarni o'rganamiz.

1. Nega bu kerak?

Bank binosini tasavvur qiling. Kirishda qo'riqchi bor. Lekin pul faqat qo'riqchiga ishonib saqlanmaydi: ichkarida kamera, kassada alohida eshik, seyfda kod, kod esa faqat ikki xodimda. Kassir seyfni ochmaydi, qo'riqchi kassaga kirmaydi. Bitta odam xato qilsa yoki aldansa — keyingi qatlam ushlaydi.

Bu bank o'ylab topgan maxsus hiyla emas. Bu — bir necha oddiy qoida, ularni har qanday tizimga qo'llash mumkin. Dasturda ham xuddi shunday qoidalar bor. Ular xavfsizlik tamoyillari (security principles) deyiladi.

Tamoyilning foydasi: yangi kod yozayotganda "bu yerda qanday hujum bo'lishi mumkin?" deb hamma hujumni eslash shart emas. "Bu yerda eng kam huquq bormi? Xato bo'lsa eshik yopiladimi?" degan bir necha savol yetadi.

2. Chuqur himoya

2.1 Bitta qatlam yetmaydi

Chuqur himoya (defense in depth) — bir himoya qatlamiga emas, bir-biridan mustaqil bir nechta qatlamga tayanish. Har qatlam boshqasining xatosini ushlaydi.

vazifalar-api da "boshqa odamning vazifasini ko'rmasin" qoidasi uchun allaqachon ikki qatlam bor:

  1. Ilova qatlami — har so'rovda WHERE egasi_id = $1 (Parametrli so'rovlar va SQL injection dan beri).
  2. Baza qatlami — Row Level Security: baza o'zi har qatorni tekshiradi.

Dasturchi bir joyda WHERE ni unutsa ham, RLS ushlaydi. Nazariyada. Oldingi darsda ko'rdik: API bazaga superuser bo'lib ulanadi va RLS ishlamaydi. Keling, buni o'z bazangizda tekshiramiz.

2.2 O'z bazangizda sinab ko'ring

Quyidagi so'rovlar kanon sxemasi va namuna ma'lumotlari bilan to'ldirilgan lokal bazada bajarildi (biz ishga tushirdik, PostgreSQL 17.6). Avval superuser postgres nomidan, WHERE siz:

sql
SELECT count(*) AS jami, count(DISTINCT egasi_id) AS egalari
FROM vazifalar;

Natija:

text
 jami | egalari
------+---------
   48 |       7
(1 row)

Yetti egasining hammasi ko'rinadi. RLS siyosatlari jadvalda turibdi, lekin superuser ularni chetlab o'tadi. Endi xuddi shu so'rov — oddiy ilova roli vazifalar_ilova nomidan, joriy foydalanuvchi o'rnatilmagan:

sql
SET ROLE vazifalar_ilova;
SELECT count(*) AS korinadi FROM vazifalar;

Natija:

text
SET
 korinadi
----------
        0
(1 row)

Nol. RLS ishladi: "kim ekansiz?" degan savolga javob yo'q — demak hech narsa ko'rinmaydi. Joriy foydalanuvchini tranzaksiya ichida o'rnatamiz (Aziz — 1):

sql
BEGIN;
SET LOCAL ROLE vazifalar_ilova;
SELECT set_config('ilova.foydalanuvchi_id', '1', true);
SELECT count(*) AS korinadi FROM vazifalar;
ROLLBACK;

Natija:

text
BEGIN
SET
 set_config
------------
 1
(1 row)

 korinadi
----------
       13
(1 row)

ROLLBACK

Faqat Azizning 13 ta vazifasi. set_config(..., true) — "faqat shu tranzaksiya uchun" degani (Row Level Security darsidagi SET LOCAL bilan bir xil).

Va hujumchi uchun eng muhimi — o'chirishga urinish:

sql
SET ROLE vazifalar_ilova;
DELETE FROM vazifalar WHERE id = 2;

Natija:

text
SET
DELETE 0

Xato ham yo'q, o'chirish ham yo'q. Baza nuqtai nazaridan bu foydalanuvchi uchun 2-vazifa umuman mavjud emas.

Xulosa: qatlam bor, lekin u faqat to'g'ri rol bilan ishlaydi. Chuqur himoya — qatlamlarni qo'yish emas, ularning haqiqatan ishlashini tekshirish.

flowchart TD
  S[So'rov] --> V{Zod validatsiya}
  V -->|xato| R1[400/422]
  V --> K{Kim? sessiya}
  K -->|yo'q| R2[401]
  K --> W{WHERE egasi_id}
  W --> L{RLS bazada}
  L -->|begona qator| R3[0 qator]
  L --> N[Natija]

Diagrammaga qarang: so'rov to'rtta tekshiruvdan o'tadi va har biri o'zi rad eta oladi. Ikkinchi qatlam ("Kim?") hozircha yo'q — uni Session-based auth darsida qo'shamiz.

Tekshirib ko'ring: Ilova kodida WHERE egasi_id bor ekan, RLS nima uchun kerak? Bu ortiqcha ish emasmi?

Javob

Kod — odam yozadi, odam esa xato qiladi. Yangi endpoint, yangi hisobot so'rovi, shoshilinch tuzatish — bittasida WHERE unutilishi mumkin. RLS bazaning o'zida turadi, u har so'rovga avtomatik qo'llanadi. Ikki mustaqil qatlam birdaniga xato qilishi ehtimoli ancha kam.

3. Eng kam huquq

Eng kam huquq (least privilege) — har dastur, xizmat yoki odam faqat o'z ishiga kerakli huquqqa ega bo'lsin, ortig'iga emas. Mehmonxona kartasi misoli: u faqat sizning xonangizni ochadi, butun qavatni emas. Kartani yo'qotsangiz — zarar bitta xona bilan cheklanadi.

Kanon sxemasidagi rollarga qarang:

sql
SELECT rolname, rolsuper, rolbypassrls
FROM pg_roles WHERE rolname LIKE 'vazifalar%' OR rolname = 'postgres'
ORDER BY rolname;

Natija:

text
       rolname        | rolsuper | rolbypassrls
----------------------+----------+--------------
 postgres             | t        | t
 vazifalar_api        | f        | f
 vazifalar_hisobot    | f        | t
 vazifalar_ilova      | f        | f
 vazifalar_migratsiya | f        | f
(5 rows)

Har rolning o'z ishi bor (Rollar va huquqlar darsida yaratgansiz):

Rol Vazifasi Nima qila olmaydi
vazifalar_migratsiya jadvallarni yaratadi va o'zgartiradi so'rovlarga xizmat qilmaydi
vazifalar_api API ulanishi, RLS ostida jadval o'chira olmaydi, RLS'ni chetlab o'tmaydi
vazifalar_hisobot faqat o'qiydi, statistikasi uchun yozolmaydi
postgres hamma narsa —

Rollar to'g'ri yaratilgan. Lekin 27-qism oxirida API postgres bilan ulanayotgan edi. Rejada eng kam huquq bor, amalda yo'q. Agar API'da biror SQL injection topilsa, hujumchi superuser huquqini oladi: hamma jadval, hamma foydalanuvchi, hatto jadvallarni o'chirish.

Bu tuzatish RBAC va resurs egaligi darsida: API vazifalar_api roli bilan ulanadi va o'lchov bilan tekshiriladi.

Maslahat: Eng kam huquq faqat baza haqida emas. API kalitiga faqat kerakli ruxsatlar, CI'ga faqat kerakli sirlar, xodimga faqat kerakli serverlar. Har safar: "bu huquq bo'lmasa, nima ishlamay qoladi?" deb so'rang. Javob "hech narsa" bo'lsa — huquqni olib tashlang.

4. Sukut bo'yicha xavfsiz

Sukut bo'yicha xavfsiz (secure by default) — hech narsa sozlanmagan holat eng yopiq holat bo'lsin. Foydalanuvchi ochishni xohlasa — o'zi ataylab ochadi. Yangi telefon misoli: ekran qulfi sukut bo'yicha yoqilgan, o'chirish uchun sozlamalarga kirish kerak.

Ikki sozlama funksiyasini solishtiring. Birinchisi hamma narsani sukut bo'yicha ochadi:

js
function sozlamaYomon(env) {
  return {
    host: env.HOST ?? "0.0.0.0",
    egasiId: Number(env.EGASI_ID ?? 1),
    cors: env.CORS_ORIGINS ?? "*",
  };
}

function sozlamaYaxshi(env) {
  const cors = (env.CORS_ORIGINS ?? "").split(",").filter(Boolean);
  if (env.NODE_ENV === "production" && cors.length === 0) {
    throw new Error("production'da CORS_ORIGINS majburiy");
  }
  return { host: env.HOST ?? "127.0.0.1", cors };
}

console.log(sozlamaYomon({}));
console.log(sozlamaYaxshi({}));
try {
  sozlamaYaxshi({ NODE_ENV: "production" });
} catch (e) {
  console.log(e.message);
}

Konsolda:

text
{ host: '0.0.0.0', egasiId: 1, cors: '*' }
{ host: '127.0.0.1', cors: [] }
production'da CORS_ORIGINS majburiy
  • 0.0.0.0 — server hamma tarmoq kartalarida tinglaydi, ya'ni tashqaridan ham ochiq. 127.0.0.1 — faqat shu kompyuterdan.
  • cors: "*" — har qanday sayt javobni o'qiy oladi. Bo'sh ro'yxat — hech kim (CORS sarlavhasi chiqmaydi).
  • Prodakshnda (production) CORS ro'yxati bo'lmasa — server umuman ishga tushmaydi.

Kanon config.ts dagi qatorlar aynan ikkinchi yo'lni tutgan (Env validatsiyasi darsida yozgansiz):

ts
// Sukut — faqat lokal: reverse proxy orqasida Node porti
// tashqariga ochilmasin
HOST: z.string().min(1).default("127.0.0.1"),
// ...
.refine((e) => e.NODE_ENV !== "production"
  || e.CORS_ORIGINS.length > 0,
  { error: "production'da CORS_ORIGINS majburiy",
    path: ["CORS_ORIGINS"] })

Telefon ekrani uchun qatorlarni bo'ldik, ma'nosi kanondagidek.

Lekin halol bo'lamiz: kanonda sukut bo'yicha ochiq bitta sozlama ham bor — EGASI_ID: Butun(1). Sozlanmasa, har so'rov 1-foydalanuvchi (Aziz) nomidan bajariladi. Bu autentifikatsiya yo'qligida vaqtinchalik yechim edi. 28-qismda u o'rnini sessiyaga beradi va sozlamadan butunlay yo'qoladi.

5. Xavfsiz yiqilish

5.1 Xato bo'lsa — eshik yopiq

Har tizim ba'zan buziladi: baza javob bermaydi, tashqi xizmatdan javob kutish vaqti tugaydi (timeout), kod undefined ga duch keladi. Bunday paytda tizim ikki yo'ldan birini tanlaydi:

  • ochiq yiqilish (fail open) — "ishlay olmadim, mayli, o'tkazib yuboraman";
  • yopiq yiqilish (fail closed, fail securely) — "ishlay olmadim, demak ruxsat yo'q".

Elektr uzilganda magazin eshigi qanday bo'ladi? Agar u qulflanib qolsa — yopiq yiqilish. Agar ochilib qolsa — ochiq. Xavfsizlik nuqtai nazaridan qulflanish kerak.

js
function ruxsatBormiOchiq(foydalanuvchi, vazifa) {
  try {
    return vazifa.egasiId === foydalanuvchi.id;
  } catch {
    return true; // "xizmat to'xtamasin"
  }
}

function ruxsatBormiYopiq(foydalanuvchi, vazifa) {
  try {
    return vazifa.egasiId === foydalanuvchi.id;
  } catch {
    return false; // shubha bo'lsa — yo'q
  }
}

const vazifa = { id: 2, egasiId: 1 };
const aziz = { id: 1 };
const malika = { id: 5 };
const mehmon = undefined; // tizimga kirmagan

for (const kim of [aziz, malika, mehmon]) {
  console.log(
    ruxsatBormiOchiq(kim, vazifa),
    ruxsatBormiYopiq(kim, vazifa),
  );
}

Konsolda:

text
true true
false false
true false

Uchinchi qatorga qarang. Tizimga kirmagan odam uchun foydalanuvchi.id TypeError beradi (try / catch / finally). Birinchi funksiya xatoni ushlab, true qaytardi: mehmon Azizning vazifasiga kirdi. Malikaga esa — yo'q. Ya'ni bu kodda tizimga kirmagan odam kirgan odamdan ko'proq huquqqa ega. Bunday xato real tizimlarda uchraydi.

5.2 Kanondagi misollar

vazifalar-api da yopiq yiqilishning ikki misolini allaqachon ko'rdingiz:

  • RLS: joriy foydalanuvchi o'rnatilmagan → joriy_foydalanuvchi() NULL qaytaradi → egasi_id = NULL hech qachon rost emas → 0 qator. Yuqoridagi korinadi = 0 aynan shu.
  • Sozlama xato → server ishga tushmaydi, exit 78 (oldingi darsdagi o'lchov). Yarim-sozlangan server ishlab turgandan ko'ra, umuman ishlamagani yaxshi.

Diqqat: Har joyda ham yopiq yiqilish to'g'ri emas. Masalan, tezlik cheklovi (rate limiting) xizmati buzilsa, butun saytni yopish — mavjudlikni (A) o'zingiz buzish. Kanon bu joyda ochiq yiqilishni ataylab tanlagan va login uchun boshqacha muqobilni ko'rsatadi — bu Tezlik cheklovi darsida. Muhimi — tanlov ongli bo'lsin, catch ichida tasodifan emas.

Tekshirib ko'ring: catch { return true; } yozgan dasturchi nima demoqchi bo'lgan? Uning niyati yomonmi?

Javob

Niyati yaxshi: "kichik xato tufayli foydalanuvchi ishlay olmay qolmasin". Lekin u mavjudlikni (A) himoya qilib, maxfiylik va yaxlitlikni (C, I) qurbon qildi. Avtorizatsiya tekshiruvida xato bo'lsa — javob doim "ruxsat yo'q" bo'lishi kerak.

6. Hech kimga ishonma

6.1 Foydalanuvchi kiritganiga ishonma

Serverga kelgan hamma narsa — foydalanuvchi qo'lida bo'lgan: tana, URL parametrlari, sarlavhalar, cookie, hatto Content-Type. Frontend ularni to'g'ri yuboradi deb o'ylash — xato. Hujumchi frontendsiz, to'g'ridan curl bilan istagan narsani yuboradi.

«Bahor» oshxonasining buyurtma API'si misoli. Frontend { taom: "osh", narx: 35000 } yuboradi. Server narxni tanadan olsa, hujumchi narx: 1 yuboradi va oshni bir so'mga oladi. To'g'risi: mijozdan faqat nima kerakligi (taom: "osh"), narx esa serverdagi menyudan.

Bu tamoyil inglizcha "never trust user input" deb yuritiladi. Uni Validatsiya darsida Zod bilan qo'llagansiz. Bugun uni kengaytiramiz: validatsiya — "bu ma'lumot to'g'ri shaklda" degani. Lekin "bu odamga shu ma'lumotni yuborishga ruxsat bor" degani emas. Ikkinchisi — avtorizatsiya, alohida tekshiruv.

6.2 Zero trust: ichkarida ham ishonma

Eski yondashuv: "tarmoq ichidagilar — o'zimiznikilar, tashqaridagilar — begona". Firma binosi atrofida devor, ichkarida hamma eshik ochiq. Muammo: hujumchi bitta kompyuterni egallasa — u allaqachon ichkarida.

Zero trust (nol ishonch) — tarmoqdagi joylashuvga qarab ishonmaslik. Har so'rov, hatto ichki xizmatdan kelgani ham, kim yuborganini isbotlashi kerak. vazifalar-api uchun bu:

  • Postgres faqat parol yoki sertifikat bilan ulanishga ruxsat beradi, "localhost'dan bo'lsa bo'ldi" emas;
  • Redis'ga ham parol (kanon lokalda parolsiz, prodakshnda bu yetmaydi);
  • bir xizmat boshqasini chaqirsa — imzo yoki kalit bilan (API kalitlar va servislararo autentifikatsiya).

Zero trust — katta tashkilotlarning arxitekturasi, unga oddiy dasturda to'liq erishish shart emas. Lekin g'oyasi har joyda foydali: "ichkaridan keldi" — dalil emas.

7. Ruxsat ro'yxati va taqiq ro'yxati

7.1 Qaysi biri xavfsizroq

Ikki yo'l bor:

  • Taqiq ro'yxati (denylist; eski kodda — blacklist) — taqiqlangan narsalarni sanash: "parol_hash maydonini qaytarma".
  • Ruxsat ro'yxati (allowlist; eski kodda — whitelist) — ruxsat etilgan narsalarni sanash: "faqat id, matn, bajarildi, muddat ni qaytar".

Oldingi qismlarda shu g'oyani boshqa nom bilan ham uchratgan bo'lishingiz mumkin. Shu qismdan boshlab bitta nom ishlatamiz: ruxsat ro'yxati (allowlist).

To'y ro'yxati misoli: qo'riqchiga "bu besh kishini kiritma" deyish — taqiq ro'yxati. "Faqat ro'yxatdagi 200 kishini kirit" — ruxsat ro'yxati. Begona odam birinchi holatda kiradi, ikkinchisida — yo'q.

js
const TAQIQLANGAN = ["parol_hash", "email"];
const taqiqBilan = (maydon) => !TAQIQLANGAN.includes(maydon);

const RUXSAT = ["id", "matn", "bajarildi", "muddat"];
const ruxsatBilan = (maydon) => RUXSAT.includes(maydon);

for (const m of ["matn", "parol_hash", "PAROL_HASH", "telefon"]) {
  console.log(m.padEnd(11), taqiqBilan(m), ruxsatBilan(m));
}

Konsolda:

text
matn        true true
parol_hash  false false
PAROL_HASH  true false
telefon     true false

Taqiq ro'yxati ikki joyda yutqazdi. PAROL_HASH — katta harf bilan yozilgan, ro'yxatda yo'q, o'tib ketdi. telefon — ertaga qo'shilgan yangi maxfiy ustun, uni ro'yxatga qo'shishni hech kim eslamadi. Ruxsat ro'yxati ikkalasini ham rad etdi: u noma'lum narsani sukut bo'yicha rad etadi. Bu — "sukut bo'yicha xavfsiz" tamoyilining o'zi.

7.2 Ruxsat ro'yxati ham to'g'ri tekshirilsin

Filtrlash, saralash va maydon tanlash darsida ?sort= maydonini ruxsat ro'yxati bilan tekshirgan edik. Kanon u yerda ataylab in emas, Object.hasOwn ishlatgan. Sababi:

js
const USTUN = {
  yaratildi: "yaratildi",
  muddat: "muddat",
  matn: "matn",
};

const inBilan = (maydon) => maydon in USTUN;
const hasOwnBilan = (maydon) => Object.hasOwn(USTUN, maydon);

for (const m of ["muddat", "parol_hash", "constructor", "toString"]) {
  console.log(m, inBilan(m), hasOwnBilan(m));
}

Konsolda:

text
muddat true true
parol_hash false false
constructor true false
toString true false

in operatori obyektning prototipidagi kalitlarni ham "bor" deydi (Obyekt: kalit-qiymat juftliklari). Har oddiy obyektda constructor va toString bor — ular Object.prototype dan keladi. Hujumchi ?sort=constructor yuborsa, in uni "ruxsat ro'yxatida" deb o'tkazadi. Keyin kod USTUN["constructor"] ni SQL'ga qo'yishga urinadi — kutilmagan xato yoki undan yomoni. Object.hasOwn faqat obyektning o'z kalitlarini tekshiradi.

Saboq: ruxsat ro'yxati — kuchli tamoyil, lekin uni tekshiradigan kod ham aniq bo'lishi kerak.

8. Tamoyillar bir jadvalda

Tamoyil Bir gapda vazifalar-api da
Chuqur himoya bir necha mustaqil qatlam Zod + WHERE + RLS
Eng kam huquq faqat kerakli ruxsat rollar bor, lekin API superuser bilan
Sukut bo'yicha xavfsiz sozlanmagan — yopiq HOST 127.0.0.1, CORS bo'sh
Xavfsiz yiqilish xato — "yo'q" RLS NULL → 0 qator, config → exit 78
Hech kimga ishonma har kirish tekshiriladi Zod sxemalari, strictObject
Zero trust ichkaridan ham isbot ichki xizmatlar kalit bilan
Ruxsat ro'yxati ruxsat etilganni sanash ?sort= — Object.hasOwn

Bitta tamoyil bor, u boshqalarning hammasini birlashtiradi: hujum yuzasini kichraytirish (oldingi darsda ko'rdik). Ishlatilmaydigan kod, marshrut, huquq, bog'liqlik bo'lmasa — uni himoya qilish ham kerak emas.

9. Hujumchi nigohi

Hujumchi tamoyillarni bilmaydi — u ularning yo'qligini qidiradi. Jadvalda: hujum, u qaysi tamoyil yo'qligidan foydalanadi, unga qarshi himoya va himoyani qanday tekshiramiz:

Hujum Tamoyil yo'q Himoya Tekshiruv
SQL injection → butun baza eng kam huquq cheklangan rol + RLS begona qator → 0 ta
sukut sozlamadagi server sukut bo'yicha xavfsiz 127.0.0.1, bo'sh CORS sozlamaYaxshi({})
catch { return true } xavfsiz yiqilish catch → false mehmon → false
narx: 1 tanada hech kimga ishonma narx serverdan narx: 1 → e'tiborsiz
PAROL_HASH, yangi ustun ruxsat ro'yxati Object.hasOwn maxfiy maydon yo'q

"Tekshiruv" ustuniga qarang: har himoyaning yonida uni sinaydigan kichik misol bor. Bu darsda ularning ko'pini o'zingiz ishga tushirdingiz. Har qatordagi hujum alohida dars mavzusi bo'ladi. Lekin ularning hammasini bitta savol to'xtatadi: "bu yerda qaysi tamoyil buzilgan?".

10. Ko'p uchraydigan xatolar

Xato Nega xavfli To'g'risi
bitta qatlamga ishonish ("bizda CORS bor") qatlam buzilsa — himoya yo'q kamida ikki mustaqil qatlam
"qulay" deb superuser bilan ulanish bitta teshik — butun baza alohida, cheklangan rol
sukut qiymati * yoki 0.0.0.0 sozlashni unutish = ochiq eshik sukut — yopiq, ochish ataylab
catch da ruxsat berish xato hujum yo'liga aylanadi xato bo'lsa — rad
narx, rol, egasiId ni tanadan olish mijoz istaganini yozadi server o'zi hisoblaydi
in bilan ruxsat ro'yxati prototip kalitlari o'tadi Object.hasOwn
taqiq ro'yxati bilan filtrlash yangi va o'zgargan qiymatlar o'tadi ruxsat ro'yxati

11. Mashqlar

Mashqlar kurs/mashqlar/28/02-tamoyillar/ da.

1-mashq (oson): qaysi tamoyil?

Har holatda qaysi tamoyil buzilgan?

  1. Yangi xodimga birinchi kundanoq hamma serverlarga kirish berildi.
  2. Admin panel parolsiz ochiladi, "keyin parol qo'yamiz" deb qoldirildi.
  3. Fayl yuklashda faqat .exe taqiqlangan.
  4. Buyurtma API'si chegirmani mijoz yuborgan chegirma maydonidan oladi.
Yechim
  1. Eng kam huquq — faqat ishiga kerakli serverlar.
  2. Sukut bo'yicha xavfsiz — sozlanmagan holat ochiq qolgan.
  3. Ruxsat ro'yxati o'rniga taqiq ro'yxati — .scr, .bat, .hta o'tib ketadi (Fayl yuklash xavfsizligi darsida aynan shu).
  4. Hech kimga ishonma — chegirmani server hisoblaydi.

2-mashq (o'rta): yopiq yiqilish

adminmi(foydalanuvchi) funksiyasini yozing: foydalanuvchi.rollar massivida "admin" bo'lsa true. Foydalanuvchi undefined bo'lsa, rollar yo'q bo'lsa yoki massiv bo'lmasa — xato tashlamasin va false qaytarsin. To'rt holatda sinang: admin, oddiy foydalanuvchi, undefined, { rollar: "admin" } (massiv emas, satr). Ishora: Array.isArray (typeof va turni to'g'ri aniqlash) va ?..

Yechim
js
function adminmi(foydalanuvchi) {
  const rollar = foydalanuvchi?.rollar;
  if (!Array.isArray(rollar)) return false;
  return rollar.includes("admin");
}

console.log(adminmi({ rollar: ["admin", "foydalanuvchi"] }));
console.log(adminmi({ rollar: ["foydalanuvchi"] }));
console.log(adminmi(undefined));
console.log(adminmi({ rollar: "admin" }));

Konsolda:

text
true
false
false
false

To'rtinchi holat — nozik tuzoq. Satrning ham includes metodi bor: "admin".includes("admin") — true. Array.isArray tekshiruvisiz bu foydalanuvchi admin bo'lib qolardi. Kutilmagan shakl — "yo'q" degan javob.

3-mashq (qiyin): ruxsat ro'yxati bilan javobni tozalash

ochiqKorinish(qator, ruxsat) funksiyasini yozing: bazadan kelgan qator obyektidan faqat ruxsat massividagi kalitlarni yangi obyektga ko'chirsin. Qatorda yo'q kalit natijaga tushmasin. Object.hasOwn bilan tekshiring. Sinang: { id: 7, matn: "Non olish", parol_hash: "$argon2id$...", telefon: "+998 90 000 00 00" } va ruxsat ["id", "matn", "muddat"]. Ishora: Object.fromEntries va filter (Object.* metodlarining to'liq sharhi).

Yechim
js
function ochiqKorinish(qator, ruxsat) {
  return Object.fromEntries(
    ruxsat
      .filter((kalit) => Object.hasOwn(qator, kalit))
      .map((kalit) => [kalit, qator[kalit]]),
  );
}

const qator = {
  id: 7,
  matn: "Non olish",
  parol_hash: "$argon2id$...",
  telefon: "+998 90 000 00 00",
};

console.log(ochiqKorinish(qator, ["id", "matn", "muddat"]));

Konsolda:

text
{ id: 7, matn: 'Non olish' }

Funksiya ruxsat ro'yxatidan yuradi, qatordan emas. Shuning uchun ertaga bazaga yangi maxfiy ustun qo'shilsa ham, u javobga tushmaydi. muddat qatorda yo'q — natijada ham yo'q (undefined bilan emas, umuman).

12. Loyiha qadami: ishonch chegaralari

Bu darsda ham kod o'zgarmaydi. Oldingi darsda docs/XAVFSIZLIK.md ning birinchi va uchinchi bo'limlarini yozgan edingiz. Ular orasiga ikkinchisini qo'shing — ishonch chegaralari va har chegaradagi uch tekshiruv. Kanon matni:

text
## 2. Ishonch chegaralari (trust boundaries)

brauzer (frontend) ──HTTPS──▶ nginx ──▶ vazifalar-api (Express) ──▶ Postgres (RLS)
       ▲                                     │                      └─▶ Redis (kesh)
 hujumchi sahifasi (CSRF)                    └──▶ tashqi: webhook URL, OIDC, SMS/email

Har chegara kesib o'tilganda: kirish tekshiriladi (Zod), kim ekanligi
aniqlanadi (autentifikatsiya), nimaga ruxsati borligi tekshiriladi
(avtorizatsiya).

Kanonda diagramma o'zgacha bo'shliqlar bilan chizilgan va qavslarda qism raqamlari bor — biz ularni olib tashladik va oxirgi gapni telefon uchun qatorlarga bo'ldik. Bu bo'lim tamoyillarning qayerda qo'llanishini ko'rsatadi: har strelka — "hech kimga ishonma" qo'llanadigan joy.

Bugungi SQL tajribasini ham hujjatga eslatma qilib yozib qo'ying: "API superuser bilan ulanganda RLS ishlamaydi (48 ta vazifa, 7 egasi); vazifalar_ilova bilan — 0, foydalanuvchi o'rnatilganda — faqat o'ziniki". Bu eslatma RBAC va resurs egaligi darsida yopiladi.

Hujjat hali to'liq emas — hali commit qilmang. Keyingi darsda unga OWASP xaritasini qo'shamiz va commit qilamiz.

13. Real ishda

  • Kod review da tamoyillar tayyor savol beradi: "bu catch nima qaytaradi?", "bu rolga shuncha huquq kerakmi?", "sukut qiymati nima?".
  • Bulut xizmatlari (AWS, Google Cloud) huquqlarni aynan eng kam huquq tamoyili bilan beradi: har xizmatga alohida, tor ruxsat. Tajribali muhandislar * (hamma narsa) ruxsatini ko'rganda darhol to'xtatadi.
  • Zero trust — Google o'zining ichki tarmog'ida BeyondCorp nomi bilan qo'llagan va sohaga keng tarqalgan yondashuv.
  • Intervyuda: "Defense in depth nima, misol keltiring", "Fail open va fail closed farqi?", "Nega allowlist denylist'dan yaxshi?".

Xulosa

  • Chuqur himoya — bir necha mustaqil qatlam; lekin har qatlam haqiqatan ishlashini tekshirish kerak (superuser bilan RLS ishlamadi).
  • Eng kam huquq — har rol faqat o'z ishiga; API uchun superuser — xato.
  • Sukut bo'yicha xavfsiz — sozlanmagan narsa yopiq (127.0.0.1, bo'sh CORS).
  • Xavfsiz yiqilish — avtorizatsiyada xato bo'lsa, javob "yo'q".
  • Hech kimga ishonma — mijoz yuborgan hamma narsa tekshiriladi; ichkaridan kelgani ham.
  • Ruxsat ro'yxati taqiq ro'yxatidan kuchliroq; uni Object.hasOwn bilan tekshiring.

Keyingi dars: OWASP Top 10:2025 xaritasi — dunyodagi eng keng tarqalgan 10 xavf toifasi, 2021-yildan nima o'zgardi va har biri vazifalar-api da qayerda; xavfsizlik hujjatini tugatib, commit qilamiz.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Xavfsizlik tamoyillari: chuqur himoya, eng kam huquq va sukut bo'yicha xavfsizlik — IlmHamroh