Mundarija (31)
- Bu darsda
- 1. Nega bu kerak?
- 2. Operator injection
- 2.1 Hujum
- 2.2 $regex bilan terish
- 2.3 Query string orqali
- 3. Himoya
- 3.1 Tur tekshiruvi — birinchi chiziq
- 3.2 Mongoose sanitizeFilter — ikkinchi chiziq
- 3.3 mongoose.trusted() — ataylab operator
- 3.4 strictQuery
- 4. Server tomonidagi JavaScript: $where va $function
- 5. Hujumchi nigohi
- 6. Autentifikatsiya va rollar
- 6.1 --auth va localhost exception
- 6.2 Eng kam huquq: readWrite va read
- 6.3 Tarmoq va TLS
- 7. Zaxira: mongodump va mongorestore
- 7.1 Zaxira olish
- 7.2 Boshqa nomga tiklash
- 7.3 --drop xavfi
- 8. Atlas Search va Vector Search (tanishuv)
- 9. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
- 10. Ko'p uchraydigan xatolar
- 11. Mashqlar
- 1-mashq (oson): to'ldiring
- 2-mashq (o'rta): sanitizeFilter modeli
- 3-mashq (qiyin): hujum va himoya
- 12. Real ishda
- Xulosa
- Manbalar
MongoDB xavfsizligi va operatsiyasi: operator injection, sanitizeFilter, rollar va mongodump
Qisqacha: MongoDB'da SQL injection yo'q, lekin operator injection bor: login so'rovi tanasiga
{"email": {"$ne": null}, "parolHash": {"$ne": null}}yuborilsa, tekshiruvsiz filtr birinchi foydalanuvchini — kanonda adminni — qaytardi. Himoya: kirish turini tekshirish (satr bo'lmasa — 400) va MongoosesanitizeFilter. Server tomonidagi JavaScript ($where,$function) MongoDB 8.0 hujjatida eskirgan deb e'lon qilingan, kanonda 8.3.13 da esa hali ishladi. Operatsiyada:--auth, eng kam huquqli rollar (readWrite,read),mongodump --gzip/mongorestore— va--dropzaxiradan keyingi ma'lumotni o'chirishini unutmang.
Bu darsda
- Operator injection'ni hujumchi ko'zi bilan ko'rasiz va ikki xil himoyani qo'llaysiz.
sanitizeFilter,mongoose.trusted()vastrictQueryni tushunasiz.$whereva$functionnima uchun xavfli ekanini bilasiz.- Autentifikatsiya, localhost exception va rollar bilan eng kam huquq tamoyilini qo'llaysiz.
mongodump/mongorestorebilan zaxira olib tiklaysiz va--droptuzog'idan qochasiz.
Oldin bilishingiz kerak: Replica set, tranzaksiyalar va change streams, Parametrli so'rovlar va SQL injection, Rollar va huquqlar, Backup va restore.
1. Nega bu kerak?
Parametrli so'rovlar va SQL injection darsida asosiy qoida edi: foydalanuvchi kiritgani — ma'lumot, kod emas. SQL'da xavf — satrni so'rovga yopishtirish. MongoDB'da so'rov satr emas, obyekt. "Demak injection yo'q" — keng tarqalgan, lekin xato fikr.
Mongo filtri obyekt, va obyekt ichida operator bo'lishi mumkin. Foydalanuvchi satr o'rniga obyekt yuborsa — u so'rovning ma'nosini o'zgartiradi. Bu NoSQL injection yoki operator injection.
Ikkinchi xavf — operatsiya. Internetda parolsiz ochiq qolgan MongoDB serverlari ma'lumot sizib chiqishining eng mashhur manbalaridan biri bo'lgan: sukut sozlama bilan ishga tushirilgan va tashqariga ochilgan bazalar. Bitta noto'g'ri mongorestore esa xuddi o'chirish kabi ma'lumotni yo'qotadi.
O'xshatish: qorovul "ismingiz Aziz bo'lsa, kiring" deydi. Hujumchi ismini aytmaydi — u "ismim — bo'sh bo'lmagan har qanday narsa" deydi. Qorovul so'zma-so'z tekshirsa, eshik ochiladi.
Chiqishlar — kanon o'lchovi (IlmHamroh, 2026-10): MongoDB 8.3.13, mongosh 2.13.0, database-tools 100.19.1, mongoose 9.11.2, Node 24.21.0; auth sinovi — vaqtinchalik alohida konteynerda. Parollar — kanon skriptidagi lokal namunalar, haqiqiy sir emas.
2. Operator injection
2.1 Hujum
Login handler — so'rov tanasidan email va parol hash'ini olib, foydalanuvchini qidiradi (soddalashtirilgan: haqiqiy login parolni hash bilan tekshiradi — Parolni xavfsiz saqlash, 28-qismda):
// ❌ Tekshiruvsiz filtr
const body = JSON.parse(
'{"email": {"$ne": null}, "parolHash": {"$ne": null}}');
await U.findOne(
{ email: body.email, parolHash: body.parolHash },
{ projection: { _id: 0, email: 1, rol: 1 } },
);Kanon kolleksiyasida ikki foydalanuvchi: aziz@example.com (rol: "admin") va dilnoza@example.com.
topildi (birinchi foydalanuvchi — admin!): { email: 'aziz@example.com', rol: 'admin' }Filtr endi "email teng emas null VA parolHash teng emas null" — har bir foydalanuvchi mos keladi, findOne birinchisini qaytardi. Birinchi yaratilgan foydalanuvchi ko'pincha admin. Hujumchi parolsiz admin bo'lib kirdi.
Express'ning express.json() (Express'ga kirish) JSON tanani aynan shunday obyektga aylantiradi — hech qanday xato yo'q.
2.2 $regex bilan terish
await U.countDocuments({ email: JSON.parse('{"$regex": "^a"}') });$regex bilan email terish: 1"a bilan boshlanadigan email bormi?" — 1. Keyin ^az, ^azi … Har javobga qarab hujumchi email'ni harfma-harf tiklaydi — parolni ham, agar u shunday tekshirilsa. Bundan tashqari, qimmat regex serverni qiynaydi (ReDoS).
2.3 Query string orqali
JSON tana yagona yo'l emas. Kanon izohi: ?email[$ne]=x — qs kutubxonasi (Express'ning extended query parser'i) undan obyekt yasaydi: { email: { $ne: "x" } }. Express 5 da sukut parser — simple va u obyekt yasamaydi (req obyekti). Kanon bu qismni Mongo bilan sinamadi — 25-qism kanoniga tayanadi. Express 4 dan ko'chirilgan yoki query parser: "extended" yoqilgan loyihada bu teshik ochiq.
sequenceDiagram
participant H as Hujumchi
participant E as Express
participant M as MongoDB
H->>E: POST /login {"email":{"$ne":null}, ...}
E->>E: express.json() — obyekt
E->>M: findOne({ email: {$ne: null}, ... })
M-->>E: birinchi foydalanuvchi (admin)
E-->>H: kirish muvaffaqiyatliDiagrammaga qarang: biror qatlam "bu satr bo'lishi kerak" deb tekshirmadi — xato shu.
3. Himoya
3.1 Tur tekshiruvi — birinchi chiziq
const xavfsiz = (b) =>
typeof b.email === "string" && typeof b.parolHash === "string"
? U.findOne({ email: b.email, parolHash: b.parolHash })
: Promise.resolve("400 — noto'g'ri so'rov");
console.log(await xavfsiz(body));400 — noto'g'ri so'rovEng ishonchli himoya: kirish shaklini tekshirish. API'da bu Zod schema'si (Zod 4 serverda): z.object({ email: z.email(), parol: z.string() }) — obyekt kelsa, validatsiya xatosi va 400. vazifalar-api da hamma kirish allaqachon Zod'dan o'tadi — Mongo'ga o'tsa ham shu qatlam himoya qiladi.
3.2 Mongoose sanitizeFilter — ikkinchi chiziq
Mongoose filtrdagi $ bilan boshlanadigan kirish obyektlarini zararsizlantira oladi:
await F.findOne({ email: body.email, parolHash: body.parolHash })
.lean(); // himoyasiz
await F.findOne({ email: body.email, parolHash: body.parolHash })
.setOptions({ sanitizeFilter: true }).lean();sanitizeFilter'siz: aziz@example.com
sanitizeFilter: true: CastError: Cast to string failed for value "{ '$ne': null }" (type Object) at path "parolHash" for model "Foydalanuvchi"Mongoose orqali ham himoyasiz so'rov Azizni qaytardi — Mongoose o'z-o'zidan himoya qilmaydi. sanitizeFilter: true bilan — CastError: "{ '$ne': null } ni satrga aylantirib bo'lmadi". Ichkarida nima bo'ldi? Biz bazasiz tekshirdik — mongoose.sanitizeFilter funksiyasi filtrni qanday o'zgartiradi:
const body = JSON.parse(
'{"email": {"$ne": null}, "parolHash": {"$ne": null}}');
console.log(JSON.stringify(mongoose.sanitizeFilter(body)));Natija (biz ishga tushirdik: mongoose 9.11.2, Node 24.21.0, ulanishsiz):
{"email":{"$eq":{"$ne":null}},"parolHash":{"$eq":{"$ne":null}}}{ $ne: null } endi operator emas — $eq ichida qiymat: "email aynan { $ne: null } obyektiga teng bo'lsin". Bunday email yo'q, hujum o'chdi. Schema'da parolHash: String bo'lgani uchun Mongoose bu obyektni satrga aylantirmoqchi bo'ldi va CastError berdi.
Global yoqish (hujjat bo'yicha): mongoose.set("sanitizeFilter", true).
3.3 mongoose.trusted() — ataylab operator
sanitizeFilter yoqilganda o'zingiz yozgan operatorlar ham o'raladi. Ishonchli operatorni mongoose.trusted() bilan belgilaysiz:
await F.findOne({ email: mongoose.trusted({ $regex: "^d" }) })
.setOptions({ sanitizeFilter: true }).lean();mongoose.trusted() — aniq ruxsat: dilnoza@example.comBazasiz tekshiruvimiz ham shuni ko'rsatdi — trusted obyekt o'zgarmay qoladi:
{"email":{"$regex":"^d"}}Qoida: trusted() ichiga hech qachon foydalanuvchi kiritganini qo'ymang — faqat kodingizdagi doimiy operatorlarni.
3.4 strictQuery
strictQuery (Mongoose 7+ sukut false): sxemada yo'q maydon filtrdan 0 ta topdi (filtr tashlanmadi)F.countDocuments({ nomalum: "x" }) — sxemada nomalum yo'q, lekin filtr tashlanmadi va bazaga ketdi: 0 natija. Mongoose 6 da strictQuery sukut true edi — noma'lum maydonli filtr jim o'chirilib, {} qolardi, ya'ni hamma hujjat. Mongoose 7 dan sukut false (hujjat bo'yicha) — xavfsizroq. Eski kodni yangilaganda bu farqni biling.
4. Server tomonidagi JavaScript: $where va $function
await U.find({ $where: "this.email.length > 16" }).toArray();
await U.find({ $expr: { $function: {
body: "function(e){ return e.length > 16 }",
args: ["$email"], lang: "js",
} } }).toArray();$where: 1 ta
$expr + $function: 1 ta$where va $function — server ichida JavaScript kodini bajaradi. MongoDB 8.0 hujjati ularni eskirgan (deprecated) deb e'lon qilgan: "MongoDB 8.0 dan server tomonidagi JavaScript funksiyalari eskirgan, ishlatilsa logga ogohlantirish yoziladi". Hujjatning hozirgi 9.0-bob sahifalarida bu ogohlantirish yo'q, faqat "boshqa operator bilan yozib bo'lmasagina ishlating" tavsiyasi qolgan (2026-10 holatiga). Kanon ko'rsatdi: 8.3.13 da ikkalasi hali ishlaydi.
Xavf ikki xil:
- Kod injection.
$wherega foydalanuvchi satri qo'shilsa (`this.ism == '${ism}'`), bu — SQL injection'ning to'liq o'xshashi: hujumchi o'z kodini serverda bajaradi. - Tezlik. JS har hujjat uchun bajariladi, indeks ishlamaydi — butun kolleksiya skani.
Qoida: $where va $function ishlatmang. Deyarli har doim oddiy operatorlar yoki $expr bilan yozish mumkin ({ $expr: { $gt: [{ $strLenCP: "$email" }, 16] } }). Server JS'ni sozlama bilan butunlay o'chirib qo'yish mumkin (security.javascriptEnabled: false — hujjat bo'yicha; sukut bo'yicha yoqiq). Ilova bu operatorlarni ishlatmasa — o'chirib qo'ying.
5. Hujumchi nigohi
| Hujum | Qanday | Himoya |
|---|---|---|
| Operator injection (JSON) | {"email": {"$ne": null}} |
Zod/tur tekshiruvi → 400; sanitizeFilter |
| Operator injection (query) | ?email[$ne]=x |
Express 5 simple parser; tur tekshiruvi |
| Ma'lumotni terish | {"$regex": "^a"} → ^az … |
Tur tekshiruvi; regex — faqat ekranlangan |
| Server JS | $where ga satr yopishtirish |
$where/$function yo'q; JS o'chiq |
| Mass assignment | {"rol": "admin"} profil yangilashda |
Ruxsat etilgan maydonlar ro'yxati (Mongoose schema) |
| Ochiq port | parolsiz 0.0.0.0:27017 |
--auth, 127.0.0.1/firewall, TLS |
| Ortiqcha huquq | ilova root bilan ulanadi |
Eng kam huquq: readWrite faqat o'z bazasiga |
Uchta qatlam bir-birini to'ldiradi: kirish shakli (Zod), so'rov qatlami (sanitizeFilter), baza qatlami (rollar). Bittasi teshilsa, qolganlari ushlaydi.
6. Autentifikatsiya va rollar
6.1 --auth va localhost exception
Kanon bazasi --auth siz edi (faqat lokal sinov). Auth'ni sinash uchun kanon vaqtinchalik alohida konteynerni --auth bilan ko'tardi, MONGO_INITDB_* o'zgaruvchilarisiz (ya'ni hech qanday foydalanuvchi yo'q):
$ [auth] db.getSiblingDB("vazifalar").v.find().toArray() (mongodb://127.0.0.1:27017/)
MongoServerError: not authorized on vazifalar to execute command { find: "v", filter: {}, lsid: { id: UUID("82f3868c-5ed4-43bc-bfae-c914ff6e5796") }, $db: "vazifalar" }Foydalanuvchisiz — not authorized ("vazifalar bazasida buyruq bajarishga ruxsat yo'q"). lsid — session id (har safar boshqacha).
Lekin hali hech qanday foydalanuvchi yo'q — birinchisini kim yaratadi? Localhost exception: foydalanuvchilar yo'q paytda, server bilan bir mashinadan (konteyner ichidan) ulangan mijoz birinchi foydalanuvchini yarata oladi:
$ [auth] db.getSiblingDB("admin").createUser({user: "admin", pwd: "namuna-admin-parol", roles: ["root"]}).ok (mongodb://127.0.0.1:27017/)
1
$ [auth] db.getSiblingDB("admin").createUser({user: "boshqa", pwd: "x", roles: []}) (mongodb://127.0.0.1:27017/)
MongoServerError: Command createUser requires authenticationBirinchi createUser — muvaffaqiyatli. Ikkinchisi — endi foydalanuvchi bor, istisno yopildi: "createUser buyrug'i autentifikatsiyani talab qiladi". Shuning uchun yangi serverda birinchi admin'ni darhol yarating — aks holda o'sha mashinadagi har qanday jarayon buni sizdan oldin qilishi mumkin.
6.2 Eng kam huquq: readWrite va read
Admin bilan ikki foydalanuvchi: ilova (api — readWrite) va hisobot (hisobot — faqat read), ikkalasi faqat vazifalar bazasida (Rollar va huquqlar darsidagi Postgres rollari bilan bir xil g'oya):
$ [auth] db.getSiblingDB("vazifalar").createUser({user: "api", pwd: "namuna-api-parol", roles: [{role: "readWrite", db: "vazifalar"}]}).ok (mongodb://admin:namuna-admin-parol@127.0.0.1:27017/?authSource=admin)
1
$ [auth] db.getSiblingDB("vazifalar").createUser({user: "hisobot", pwd: "namuna-hisobot-parol", roles: [{role: "read", db: "vazifalar"}]}).ok (mongodb://admin:namuna-admin-parol@127.0.0.1:27017/?authSource=admin)
1authSource=admin — admin foydalanuvchisi admin bazasida yaratilgan, autentifikatsiya o'sha yerda.
Endi har biri nima qila oladi:
$ [auth] db.v.insertOne({matn: "api yozdi"}).acknowledged (mongodb://api:namuna-api-parol@127.0.0.1:27017/vazifalar)
true
$ [auth] db.v.insertOne({matn: "hisobot yozmoqchi"}) (mongodb://hisobot:namuna-hisobot-parol@127.0.0.1:27017/vazifalar)
MongoServerError: not authorized on vazifalar to execute command { insert: "v", documents: [ { matn: "hisobot yozmoqchi", _id: ObjectId('6add9e3f1a1fac541a4002c0') } ], ordered: true, lsid: { id: UUID("05f7f102-2011-4230-b33f-bed148e9ed9c") }, $db: "vazifalar" }
$ [auth] db.v.countDocuments() (mongodb://hisobot:namuna-hisobot-parol@127.0.0.1:27017/vazifalar)
1
$ [auth] db.getSiblingDB("admin").system.users.find().toArray() (mongodb://api:namuna-api-parol@127.0.0.1:27017/vazifalar)
MongoServerError: not authorized on admin to execute command { find: "system.users", filter: {}, lsid: { id: UUID("98dd8456-9c49-416c-a062-0d704fb79a39") }, $db: "admin" }
$ [auth] db.v.countDocuments() (mongodb://api:notogri@127.0.0.1:27017/vazifalar)
MongoServerError: Authentication failed.| Kim | Amal | Natija |
|---|---|---|
api (readWrite) |
yozish | ruxsat |
hisobot (read) |
yozish | not authorized |
hisobot |
o'qish | ruxsat (1 hujjat) |
api |
admin.system.users ni o'qish |
not authorized |
api, noto'g'ri parol |
har qanday | Authentication failed. |
Ilova SQL injection yoki operator injection orqali buzilsa ham, api foydalanuvchisi boshqa bazalarga, foydalanuvchilar ro'yxatiga va server sozlamalariga tega olmaydi. root bilan ulangan ilova esa buzilsa — hamma narsa hujumchida.
Parollarni bu yerdagidek ulanish satriga yozish — faqat lokal namuna. Haqiqiy loyihada parol .env da yoki sirlar omborida (Sirlarni boshqarish), kodda va git'da emas. Ulanish satridagi parolda maxsus belgilar (@, :, /) bo'lsa — URL-kodlash (encodeURIComponent).
Tekshirib ko'ring: Hisobot servisi faqat statistika o'qiydi. Unga
readWritebersangiz, qanday xavf bor?
Javob
Hisobot servisi buzilsa yoki unda xato bo'lsa (masalan, noto'g'ri deleteMany), u asosiy ma'lumotni o'zgartira yoki o'chira oladi. read roli bilan eng yomon holat — ma'lumot o'qilishi; o'zgartirish imkonsiz. Kanon ko'rsatdi: read foydalanuvchining yozishi not authorized bilan to'xtadi.
6.3 Tarmoq va TLS
Hujjat bo'yicha (kanon sinamadi): bindIp — server qaysi manzillarda tinglaydi (kanonda port faqat 127.0.0.1 ga ochilgan); firewall bilan faqat ilova serverlariga ruxsat; tarmoq orqali ulanishda TLS (tls=true). Atlas'da bular sukut bo'yicha yoqiq va IP ruxsat ro'yxati majburiy.
7. Zaxira: mongodump va mongorestore
7.1 Zaxira olish
mongodump — bazani BSON fayllarga eksport qiladi (Postgres'dagi pg_dump — Backup va restore). Kanon vazifalar bazasini konteyner ichida siqib oldi (qatorlar bo'lingan):
docker exec kanon27-mongo sh -c 'rm -rf /tmp/zaxira &&
mongodump --quiet \
--uri="mongodb://127.0.0.1:27017/?directConnection=true" \
--db=vazifalar --gzip --out=/tmp/zaxira'-rw-r--r-- 1 root root 541 Oct 25 06:14 foydalanuvchilar.bson.gz
-rw-r--r-- 1 root root 158 Oct 25 06:14 foydalanuvchilar.metadata.json.gz
-rw-r--r-- 1 root root 67 Oct 25 06:14 prelude.json.gz
-rw-r--r-- 1 root root 3194 Oct 25 06:14 vazifalar.bson.gz
-rw-r--r-- 1 root root 186 Oct 25 06:14 vazifalar.metadata.json.gz
28K /tmp/zaxira(ls -la chiqishining fayl qatorlari; sana-vaqt — sizda boshqacha.) Har kolleksiya uchun ikki fayl: .bson.gz — hujjatlar, .metadata.json.gz — indekslar va sozlamalar. Butun zaxira — 28 KB (--gzip bilan).
7.2 Boshqa nomga tiklash
docker exec kanon27-mongo sh -c 'mongorestore \
--uri="mongodb://127.0.0.1:27017/?directConnection=true" \
--gzip --nsFrom="vazifalar.*" --nsTo="tiklash.*" /tmp/zaxira'2026-10-25T06:14:06.485+0000 no indexes to restore for collection `tiklash.foydalanuvchilar`
2026-10-25T06:14:06.623+0000 56 document(s) restored successfully. 0 document(s) failed to restore.--nsFrom/--nsTo — tiklashda nomni almashtirish: vazifalar.* → tiklash.*. Asl bazaga tegmasdan zaxirani tekshirishning to'g'ri yo'li. 56 hujjat = 8 foydalanuvchi + 48 vazifa. "tiklash.foydalanuvchilar uchun tiklanadigan indeks yo'q" — u kolleksiyada _id dan boshqa indeks yo'q edi.
$ mongosh --eval 'db.getSiblingDB("tiklash").vazifalar.countDocuments() + " / indekslar: " + db.getSiblingDB("tiklash").vazifalar.getIndexes().length'
48 / indekslar: 248 hujjat va 2 indeks (_id va aggregation darsida yaratilgan { egasi, bajarildi }) — indekslar ham tiklandi.
7.3 --drop xavfi
Kanon uch qadam qildi. Avval tiklangan bazaga yangi hujjat qo'shdi ("zaxiradan keyin" yozilgan ma'lumot) — 49 ta bo'ldi:
$ mongosh --eval 'db.getSiblingDB("tiklash").vazifalar.insertOne({matn: "zaxiradan keyin"}); db.getSiblingDB("tiklash").vazifalar.countDocuments()'
49Keyin xuddi shu zaxirani --drop bilan qayta tikladi (--quiet — chiqishsiz):
docker exec kanon27-mongo sh -c 'mongorestore --quiet \
--uri="mongodb://127.0.0.1:27017/?directConnection=true" \
--gzip --drop --nsFrom="vazifalar.*" --nsTo="tiklash.*" /tmp/zaxira'Oxirida "zaxiradan keyin" hujjatini qidirdi:
$ mongosh --eval 'db.getSiblingDB("tiklash").vazifalar.countDocuments({matn: "zaxiradan keyin"})'
0--drop — tiklashdan oldin kolleksiyani o'chiradi. Zaxiradan keyin yozilgan hamma narsa (bu yerda bitta hujjat, prodakshnda — bir kunlik buyurtmalar) yo'qoldi. Bu Postgres'dagi pg_dump --clean restore xavfining o'zi — loyiha qoidalarimizda ham "to'liq restore qilmang, faqat kerakli qatorlarni ko'chiring" deyilgan.
To'g'ri tartib: (1) boshqa nomga tiklash (--nsTo), (2) kerakli hujjatlarni tekshirish, (3) faqat ularni asl bazaga ko'chirish.
Kanon zaxirani faqat shu bazada sinadi. Hujjat bo'yicha: mongodump katta bazalarda sekin va bir lahzalik izchil emas (replica set'da --oplog bilan izchilroq); katta tizimlarda disk snapshot'lari yoki Atlas'ning uzluksiz zaxirasi ishlatiladi. Zaxira tiklab ko'rilmaguncha zaxira emas — buni Backup va restore darsida ham aytgandik.
8. Atlas Search va Vector Search (tanishuv)
Kanon sinamadi — faqat Atlas'da bor (hujjat bo'yicha, 2026-10-26 holatiga):
- Atlas Search — Lucene asosidagi to'liq matn qidiruvi. Imkoniyatlari: xato yozuvga chidamlilik (fuzzy), avtomatik to'ldirish, til analizatorlari, relevantlik bo'yicha saralash. MongoDB indekslari darsidagi text indeksning cheklovlarini (o'zbek tili, apostrof, sukut OR) hal qiladi.
$searchaggregation bosqichi bilan. - Atlas Vector Search — embedding vektorlari bo'yicha "ma'no bo'yicha" qidiruv: AI yordamchilar, RAG, o'xshash mahsulotlar.
$vectorSearchbosqichi. Postgres'da buning o'xshashi —pgvectorkengaytmasi (Extension'lar).
Ikkalasi ham ma'lumotni alohida qidiruv tizimiga ko'chirmasdan, Mongo ichida ishlatish imkonini beradi. Narxi — Atlas'ga bog'lanish.
9. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
vazifalar-api Postgres'da: kirish Zod bilan tekshiriladi, SQL parametrli, rollar alohida (Rollar va huquqlar), zaxira — pg_dump. Kanon Mongo'da:
- Xavfsizlik:
$neinjection → admin;$regexterish; tur tekshiruvi → 400;sanitizeFilter→CastError;trusted();strictQueryfalse;$where/$function8.3 da ishlaydi. - Operatsiya:
mongodump --gzip(28 KB),--nsFrom/--nsTobilan 56 hujjat,--dropzaxiradan keyingi hujjatni o'chirdi;--auth: localhost exception,readWrite/read,Authentication failed.
Biz qo'shganimiz: mongoose.sanitizeFilter va trusted ning ichki natijasi (bazasiz).
10. Ko'p uchraydigan xatolar
| Xato | Oqibat | To'g'risi |
|---|---|---|
req.body maydonlari to'g'ridan filtrga |
Operator injection, admin'ga kirish | Zod; sanitizeFilter |
| "Mongo'da injection yo'q" | Himoya yozilmaydi | Har kirish — tur tekshiruvi |
$where ga satr yopishtirish |
Serverda begona kod | $where ishlatmang |
Ilova root bilan ulanadi |
Buzilsa — butun server | readWrite faqat o'z bazasiga |
| Parolsiz va tashqariga ochiq port | Ma'lumot sizib chiqadi | --auth, 127.0.0.1, firewall, TLS |
Asl bazaga mongorestore --drop |
Zaxiradan keyingi ma'lumot yo'qoladi | --nsTo bilan boshqa nomga |
| Zaxirani hech qachon tiklab ko'rmaslik | Kerak paytda ishlamaydi | Muntazam tiklash sinovi |
| Parol ulanish satrida, git'da | Sir sizib chiqadi | .env / sirlar ombori |
11. Mashqlar
Mashqlar kurs/mashqlar/27/52-xavfsizlik/ da. 2- va 3-mashq yechimlari Node 24 da tekshirilgan (biz ishga tushirdik, bazasiz).
1-mashq (oson): to'ldiring
{"email": {"$ne": null}}hujumining nomi — operator- Mongoose'da
$li kirishni$eqga o'raydigan sozlama — - Faqat o'qish huquqi beradigan rol —
- Birinchi foydalanuvchini parolsiz yaratish imkoniyati — localhost
- Tiklashdan oldin kolleksiyani o'chiradigan
mongorestorebayrog'i —--
Yechim
- injection — operator injection (NoSQL injection).
- sanitizeFilter —
trusted()bilan istisno. - read — kanonda yozish
not authorized. - exception — faqat foydalanuvchi yo'q paytda va o'sha mashinadan.
- drop — zaxiradan keyingi ma'lumot yo'qoladi.
2-mashq (o'rta): sanitizeFilter modeli
tozala(filtr) funksiyasini yozing — Mongoose sanitizeFilter ning modeli: filtrning har maydonida qiymat obyekt bo'lsa va uning biror kaliti $ bilan boshlansa — qiymatni { $eq: qiymat } ga o'rasin. ishonchli(obj) bilan belgilangan obyektlar o'ralmasin (belgi — Symbol). Kanon hujum tanasi va $regex misoli bilan sinang — natija bazasiz Mongoose chiqishi bilan bir xil bo'lsin.
Ishora: belgi uchun const ISHONCHLI = Symbol("ishonchli") va obyektga [ISHONCHLI]: true xususiyati; JSON.stringify Symbol kalitlarni chiqarmaydi.
Yechim
const ISHONCHLI = Symbol("ishonchli");
const ishonchli = (obj) => ({ ...obj, [ISHONCHLI]: true });
function tozala(filtr) {
const natija = {};
for (const [k, v] of Object.entries(filtr)) {
const operatorli = v !== null && typeof v === "object"
&& Object.keys(v).some((x) => x.startsWith("$"));
natija[k] = operatorli && !v[ISHONCHLI] ? { $eq: v } : v;
}
return natija;
}
const body = JSON.parse(
'{"email": {"$ne": null}, "parolHash": {"$ne": null}}');
console.log(JSON.stringify(tozala(body)));
console.log(JSON.stringify(
tozala({ email: ishonchli({ $regex: "^d" }) })));
console.log(JSON.stringify(
tozala({ email: "aziz@example.com", parolHash: "h1" })));Konsolda:
{"email":{"$eq":{"$ne":null}},"parolHash":{"$eq":{"$ne":null}}}
{"email":{"$regex":"^d"}}
{"email":"aziz@example.com","parolHash":"h1"}Birinchi ikki qator — «Mongoose sanitizeFilter» bo'limidagi haqiqiy Mongoose natijasi bilan aynan bir xil. Uchinchisi: oddiy satrlar o'zgarmaydi — himoya halol so'rovga xalaqit bermaydi. Haqiqiy Mongoose bundan ko'proq ishni qiladi (ichma-ich $and/$or, massivlar).
3-mashq (qiyin): hujum va himoya
Xotiradagi "kolleksiya" va kichik filtr mos kelishini yozing: mos(hujjat, filtr) — tenglik va $ne operatori. Keyin ikki login funksiyasi: loginZaif(body) — body maydonlarini to'g'ridan filtrga qo'yadi; loginXavfsiz(body) — email va parolHash satr bo'lmasa "400", aks holda qidiradi. Kanon hujumini ($ne: null) va to'g'ri loginni ikkala funksiyada sinang.
Ishora: find — birinchi mos hujjat (Mongo'dagi findOne kabi).
Yechim
const foydalanuvchilar = [
{ email: "aziz@example.com", parolHash: "h1", rol: "admin" },
{ email: "dilnoza@example.com", parolHash: "h2", rol: "oddiy" },
];
function mos(h, filtr) {
return Object.entries(filtr).every(([k, shart]) => {
if (shart !== null && typeof shart === "object") {
if ("$ne" in shart) return h[k] !== shart.$ne;
return false;
}
return h[k] === shart;
});
}
const findOne = (filtr) =>
foydalanuvchilar.find((h) => mos(h, filtr)) ?? null;
function loginZaif(body) {
const u = findOne({ email: body.email, parolHash: body.parolHash });
return u ? `kirdi: ${u.email} (${u.rol})` : "401";
}
function loginXavfsiz(body) {
if (typeof body.email !== "string"
|| typeof body.parolHash !== "string") return "400";
return loginZaif(body);
}
const hujum = JSON.parse(
'{"email": {"$ne": null}, "parolHash": {"$ne": null}}');
const halol = { email: "dilnoza@example.com", parolHash: "h2" };
console.log("zaif, hujum:", loginZaif(hujum));
console.log("xavfsiz, hujum:", loginXavfsiz(hujum));
console.log("xavfsiz, halol:", loginXavfsiz(halol));
console.log("xavfsiz, xato parol:",
loginXavfsiz({ ...halol, parolHash: "x" }));Konsolda:
zaif, hujum: kirdi: aziz@example.com (admin)
xavfsiz, hujum: 400
xavfsiz, halol: kirdi: dilnoza@example.com (oddiy)
xavfsiz, xato parol: 401Birinchi qator — kanondagi hujum natijasi: parolsiz admin. Tur tekshiruvi hujumni 400 bilan to'xtatdi, halol login esa ishlayveradi. Haqiqiy loyihada typeof o'rniga Zod schema'si va parolni hash bilan solishtirish (28-qismda) — g'oya bir xil.
12. Real ishda
- Operator injection — Node + Mongo loyihalarining xavfsizlik auditlarida eng ko'p topiladigan muammolardan biri; Zod/Joi validatsiya va
sanitizeFilter— standart javob. - Rollar — har servis o'z foydalanuvchisi bilan: API —
readWrite, hisobot —read, zaxira —backuproli; admin faqat qo'lda. - Zaxira — Atlas'da avtomatik va uzluksiz; o'z serverida —
mongodumpcron bilan + muntazam tiklash sinovi. - Intervyu savollari: "MongoDB'da injection bormi?", "
sanitizeFilternima qiladi?", "eng kam huquq tamoyili Mongo'da qanday?", "mongorestore --dropxavfi?".
Xulosa
- Mongo'da SQL injection yo'q, lekin operator injection bor:
{"$ne": null}kanonda admin'ga parolsiz kirdi;$regexbilan ma'lumot terish mumkin. - Himoya qatlamlari: tur tekshiruvi/Zod (400), Mongoose
sanitizeFilter($eqga o'raydi,trusted()— istisno),strictQuerysukut false. $where/$function— 8.0 hujjatida eskirgan deb e'lon qilingan, 8.3 da ishlaydi; ishlatmang.--auth, localhost exception (faqat birinchi foydalanuvchi), eng kam huquq:readWrite/readfaqat o'z bazasiga.mongodump --gzip→mongorestore --nsFrom/--nsTo(boshqa nomga);--dropzaxiradan keyingi ma'lumotni o'chiradi.- Atlas Search va Vector Search — Atlas'dagi qidiruv imkoniyatlari (sinalmagan).
Keyingi dars: Redis nima va ulanish — xotiradagi kalit-qiymat ombori, Docker'da Redis 8 va Node'dan node-redis bilan ulanish.
Manbalar
- MongoDB 8 hujjati — Security Checklist: https://www.mongodb.com/docs/manual/administration/security-checklist/
- MongoDB hujjati — Localhost Exception, Built-In Roles: https://www.mongodb.com/docs/manual/core/localhost-exception/ , https://www.mongodb.com/docs/manual/reference/built-in-roles/
- MongoDB 8.0 hujjati —
$where("Server-side JavaScript Deprecated"): https://www.mongodb.com/docs/v8.0/reference/operator/query/where/ ; hozirgi sahifa: https://www.mongodb.com/docs/manual/reference/operator/query/where/ (2026-10-27 da o'qildi) - Mongoose hujjati —
sanitizeFilter,trusted: https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.sanitizeFilter() - MongoDB Database Tools — mongodump, mongorestore: https://www.mongodb.com/docs/database-tools/
- MongoDB Atlas Search va Vector Search: https://www.mongodb.com/docs/atlas/atlas-search/ , https://www.mongodb.com/docs/atlas/atlas-vector-search/ (2026-10-26 holatiga; sinalmagan)
- Kanon o'lchovi (IlmHamroh, 2026-10): MongoDB 8.3.13, mongosh 2.13.0, database-tools 100.19.1, mongoose 9.11.2, Node 24.21.0.
sanitizeFilterbazasiz sinovi va mashq yechimlari — biz ishga tushirdik (mongoose 9.11.2, Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!