Veb
SQL injection ORM davrida: zaiflik qayerda yashirinib qoladi
ORM SQL injection'dan to'liq himoya qilmaydi. whereRaw, orderBy, LIKE va xom so'rovlardagi xavfli joylar, ularni qanday tekshirish va Laravel hamda Node'da qanday tuzatish.
· 10 daq. o'qish

SQL injection — eng eski va eng yaxshi o'rganilgan zaifliklardan biri. Ko'pchilik dasturchilar uni o'tmishga tegishli muammo deb hisoblaydi: "Biz Eloquent ishlatamiz", "Prisma hamma narsani o'zi ekranlaydi", "bizda qo'lda yozilgan SQL yo'q". Haqiqatan ham, zamonaviy ORM'lar odatiy so'rovlarni parametrlangan holda yuboradi va oddiy holatlarda injection'ga yo'l qo'ymaydi.
Lekin amalda zaiflik yo'qolmadi — u boshqa joylarga ko'chdi. Endi u whereRaw ichidagi bitta satrda, foydalanuvchi tanlaydigan saralash ustunida, hisobot uchun tez yozilgan xom so'rovda yoki LIKE qidiruvida yashiringan bo'ladi. Ushbu maqolada ORM davrida SQL injection qayerda paydo bo'lishini, uni o'zingiz qanday tekshirishingiz va qanday yopishingiz mumkinligini ko'rib chiqamiz.
Muammoning mohiyati
SQL injection foydalanuvchi kiritgan ma'lumot SQL so'rovining kodi sifatida talqin qilinganda yuzaga keladi. Ma'lumot va buyruq bir satrga qo'shib yuborilsa, ma'lumot ichidagi qo'shtirnoq yoki kalit so'z so'rov tuzilishini o'zgartiradi.
Klassik misol:
$sql = "SELECT * FROM users WHERE email = '" . $request->email . "'";
DB::select($sql);
Agar email maydoniga ' OR '1'='1 yuborilsa, shart har doim rost bo'ladi. Murakkabroq so'rovlar bilan boshqa jadvallardan ma'lumot o'qish, ba'zan esa yozish ham mumkin.
Himoyaning asosiy tamoyili o'zgarmagan: ma'lumot va so'rov kodi alohida yuborilishi kerak. Buni prepared statement (parametrlangan so'rov) ta'minlaydi — ma'lumotlar bazasi so'rov tuzilishini oldindan oladi, qiymatlar esa keyin, alohida keladi va hech qachon kod sifatida bajarilmaydi.
ORM'lar aynan shu ishni sizning o'rningizga bajaradi. Muammo ORM'dan chetga chiqqan joyda boshlanadi.
Nima uchun bu ekspertiza uchun muhim
SQL injection OWASP Top 10 (2021) ro'yxatida A03 "Injection" toifasiga kiradi va CWE-89 sifatida tasniflanadi. Rasmiy kiberxavfsizlik ekspertizasida veb-ilova tekshirilganda injection sinovlari deyarli har doim bo'ladi: qidiruv maydonlari, filtrlar, saralash parametrlari, API endpoint'lari va formalar avtomatik vositalar hamda qo'lda sinab ko'riladi.
Bunday topilma odatda yuqori xavf darajasida baholanadi, chunki u ma'lumotlar bazasidagi barcha ma'lumotlarga — foydalanuvchilar, parol xeshlari, shaxsiy va moliyaviy ma'lumotlarga yo'l ochishi mumkin. Shuning uchun ekspertizadan oldin bu turdagi joylarni o'zingiz topib yopish ancha arzon tushadi.
ORM davrida zaiflik qayerda yashirinadi
1. Xom so'rovlar va "Raw" metodlar
Laravel'da DB::select, DB::statement, whereRaw, selectRaw, orderByRaw, havingRaw, DB::raw — bularning hammasi SQL'ni to'g'ridan-to'g'ri qabul qiladi. Ular xavfli emas, agar qiymatlar binding orqali uzatilsa. Xavf satrni birlashtirish (concatenation) yoki interpolatsiyada:
// Xavfli
User::whereRaw("YEAR(created_at) = $year")->get();
// Xavfsiz
User::whereRaw('YEAR(created_at) = ?', [$year])->get();
Node'da ham xuddi shunday. Sequelize'da sequelize.query(), Knex'da knex.raw(), Prisma'da $queryRawUnsafe va $executeRawUnsafe xavfli tomonga ochilgan eshiklar:
// Xavfli
await prisma.$queryRawUnsafe(`SELECT * FROM orders WHERE status = '${status}'`);
// Xavfsiz: tagged template qiymatlarni parametrga aylantiradi
await prisma.$queryRaw`SELECT * FROM orders WHERE status = ${status}`;
Prisma'dagi $queryRaw tagged template sifatida chaqirilganda qiymatlarni parametrlaydi, $queryRawUnsafe esa yo'q — nomi ham shuni aytib turibdi.
2. Saralash ustuni va yo'nalishi (orderBy)
Bu ORM davridagi eng keng tarqalgan injection joylaridan biri. Jadval sahifasida foydalanuvchi "Sana bo'yicha", "Summa bo'yicha" saralaydi, frontend esa ?sort=created_at&dir=desc yuboradi. Dasturchi buni to'g'ridan-to'g'ri uzatadi:
$query->orderBy($request->sort, $request->dir);
Muammo shundaki, ustun nomlari va ASC/DESC kalit so'zlari prepared statement orqali parametrlanmaydi — bu SQL'ning o'zi bunga ruxsat bermaydi. Framework identifikatorni qo'shtirnoqqa oladi, lekin bunga to'liq tayanish to'g'ri emas, ayniqsa orderByRaw yoki JSON ustunlar bilan ishlaganda. To'g'ri yechim — ruxsat etilgan qiymatlar ro'yxati (allowlist):
$allowed = ['created_at', 'amount', 'status'];
$sort = in_array($request->sort, $allowed, true) ? $request->sort : 'created_at';
$dir = $request->dir === 'asc' ? 'asc' : 'desc';
$query->orderBy($sort, $dir);
Xuddi shu qoida jadval nomlari, ustunlar ro'yxati (select), groupBy va dinamik filtr maydonlari uchun ham amal qiladi: foydalanuvchidan kelgan identifikator faqat oldindan belgilangan ro'yxatdan tanlanishi kerak.
3. LIKE va qidiruv
LIKE bilan qidiruvda ikkita muammo bor. Birinchisi — klassik injection, agar so'rov qo'lda yig'ilsa:
// Xavfli
DB::select("SELECT * FROM products WHERE name LIKE '%$q%'");
Ikkinchisi — wildcard injection. Hatto parametrlangan so'rovda ham % va _ belgilari maxsus ma'noga ega. Foydalanuvchi % yuborsa, barcha yozuvlar qaytadi; uzun %_%_%_ naqshlari esa bazaga og'ir yuk berishi mumkin. Bu SQL injection emas, lekin ma'lumot ochiqlanishi yoki xizmat sekinlashuviga olib kelishi mumkin. Belgilarni ekranlang:
$term = str_replace(['\\', '%', '_'], ['\\\\', '\%', '\_'], $request->q);
Product::where('name', 'like', "%{$term}%")->get();
PostgreSQL'da ILIKE uchun ham shu qoida amal qiladi.
4. Massivlar va IN
whereIn xavfsiz, lekin ba'zan dasturchilar ro'yxatni satrga aylantirib, xom so'rovga qo'shadi:
// Xavfli
DB::select("SELECT * FROM users WHERE id IN (" . implode(',', $request->ids) . ")");
// Xavfsiz
User::whereIn('id', array_map('intval', $request->ids))->get();
5. JSON so'rovlar va operatorlar
NoSQL bazalarda (MongoDB) injection boshqacha ko'rinishda bo'ladi: so'rovga foydalanuvchidan obyekt kirsa, {"$ne": null} kabi operatorlar shartni chetlab o'tadi. Express'da req.body.password satr emas, obyekt bo'lishi mumkin. Kiruvchi ma'lumotlarning turini tekshiring (masalan, zod yoki Joi bilan) va operatorlarni filtrlang.
6. Saqlangan protseduralar va ikkinchi darajali injection
Ma'lumot bazaga xavfsiz yozilgan bo'lishi mumkin, lekin keyinroq boshqa joyda — masalan, hisobot generatorida yoki saqlangan protsedura ichida — xom SQL'ga qo'shilsa, injection shu yerda sodir bo'ladi. Bu "second-order" injection deyiladi. Qoida oddiy: bazadan o'qilgan ma'lumot ham ishonchli emas, uni ham parametr sifatida uzating.
ORM himoya qiladi, lekin faqat siz uning ichida qolsangiz. Har bir "Raw" va har bir dinamik identifikator — alohida tekshirilishi kerak bo'lgan joy.
O'zingiz qanday tekshirishingiz mumkin
Kodni qidirish
Birinchi qadam — xavfli naqshlarni kod bazasidan topish. Laravel loyihasi uchun:
grep -rnE "whereRaw|selectRaw|orderByRaw|havingRaw|groupByRaw|DB::raw|DB::select|DB::statement|DB::unprepared" app/ routes/
grep -rnE "orderBy\(\\\$request|orderBy\(request\(" app/
Node loyihasi uchun:
grep -rnE "queryRawUnsafe|executeRawUnsafe|sequelize\.query|knex\.raw|\.query\(\`" src/
Har bir topilgan joyda savol bering: so'rovga foydalanuvchidan kelgan qiymat qo'shilyaptimi? Agar ha bo'lsa — binding yoki allowlist bormi?
Qo'lda sinov
O'z test muhitingizda parametrlarga oddiy belgilar yuborib, ilovaning xatti-harakatini kuzating:
'(bitta qo'shtirnoq) — 500 xatosi yoki SQL xato matni chiqsa, bu kuchli belgi.?sort=created_atbilan?sort=nonexistentjavoblarini solishtiring.?sort=(SELECT 1)yoki?dir=asc,idkabi qiymatlar xato qaytarsa, identifikator tekshirilmayapti.- Qidiruvga
%va_yuboring — barcha yozuvlar chiqyaptimi?
Production'da APP_DEBUG=false bo'lishi kerak, aks holda SQL xato matnlari foydalanuvchiga ko'rinadi va bu hujumchiga katta yordam beradi. Bu haqida batafsil: debug rejim va ma'lumot ochiqlanishi.
sqlmap — ehtiyotkorlik bilan
sqlmap — SQL injection'ni avtomatik aniqlaydigan ochiq manbali vosita. U kuchli, lekin ehtiyot choralari muhim:
- Uni faqat o'zingizga tegishli yoki yozma ruxsat olingan tizimlarda ishlating. Begona tizimni ruxsatsiz skanerlash qonunbuzarlik hisoblanadi.
- Production'da emas, staging yoki test nusxasida ishlating. sqlmap ko'p so'rov yuboradi va ba'zi texnikalar ma'lumotni o'zgartirishi mumkin.
- Avval past darajadan boshlang:
sqlmap -u "https://staging.example.uz/products?sort=name&q=test" \
--batch --level=1 --risk=1 -p sort,q
--risk ni oshirish UPDATE va OR asosidagi sinovlarni yoqadi, bu ma'lumotga zarar yetkazishi mumkin. Autentifikatsiya talab qilinadigan sahifalar uchun --cookie bilan test akkauntining sessiyasini bering. Topilgan har bir natijani qo'lda tasdiqlang.
Qanday tuzatish
Laravel
- Iloji boricha Eloquent va Query Builder metodlaridan foydalaning:
where,whereIn,whereBetween,whereDate. - Har bir
*Rawchaqiruvida qiymatlarni?yoki nomlangan binding bilan uzating. - Dinamik ustunlar va yo'nalishlar uchun allowlist ishlating.
- Kiruvchi ma'lumotlarni Form Request orqali validatsiya qiling:
public function rules(): array
{
return [
'sort' => ['nullable', Rule::in(['created_at', 'amount', 'status'])],
'dir' => ['nullable', Rule::in(['asc', 'desc'])],
'q' => ['nullable', 'string', 'max:100'],
'ids' => ['array', 'max:100'],
'ids.*'=> ['integer'],
];
}
DB::unpreparedni foydalanuvchi ma'lumoti bilan hech qachon ishlatmang.
Node (Express)
const allowedSort = new Set(['created_at', 'amount', 'status']);
const sort = allowedSort.has(req.query.sort) ? req.query.sort : 'created_at';
const dir = req.query.dir === 'asc' ? 'asc' : 'desc';
// Knex: qiymat binding bilan, identifikator allowlist bilan
const rows = await knex('orders')
.where('user_id', req.user.id)
.andWhere('status', req.query.status)
.orderBy(sort, dir);
// pg drayveri bilan xom so'rov
await pool.query('SELECT * FROM orders WHERE id = $1', [req.params.id]);
Ma'lumotlar bazasi darajasida
Injection bo'lgan taqdirda ham zararni kamaytirish uchun:
- Ilova bazaga superuser (
postgres,root) sifatida ulanmasin. Unga faqat kerakli jadvallar va amallarga huquq bering. - Faqat o'qiydigan hisobot modullari uchun alohida read-only foydalanuvchi oching.
- PostgreSQL'da
COPY ... PROGRAM, MySQL'daFILEhuquqi ilova foydalanuvchisida bo'lmasin. - Ma'lumotlar bazasi porti internetga ochiq bo'lmasin.
Bu "eng kam huquq" tamoyili injection'ni yo'q qilmaydi, lekin uning oqibatini sezilarli cheklaydi.
Xatolarni yashirish
Production'da SQL xato matni foydalanuvchiga chiqmasligi kerak. Laravel'da APP_DEBUG=false, Express'da xatolar uchun umumiy handler qo'ying va tafsilotlarni faqat logga yozing. Xato matni yo'qligi zaiflikni yopmaydi, lekin uni topishni ancha qiyinlashtiradi — haqiqiy tuzatish baribir binding va allowlist.
Qisqa checklist
- Kod bazasida barcha
*Raw,DB::select,queryRawUnsafe,knex.rawchaqiruvlari ko'rib chiqilgan. - Har bir xom so'rovda qiymatlar binding orqali uzatiladi, satr birlashtirilmaydi.
- Saralash ustuni, yo'nalish va dinamik maydonlar allowlist orqali tekshiriladi.
-
LIKEqidiruvida%va_ekranlanadi, uzunlik cheklangan. -
INro'yxatlariwhereInbilan, turi tekshirilgan holda uzatiladi. - Kiruvchi ma'lumotlar Form Request yoki sxema bilan validatsiya qilinadi.
- Ilovaning baza foydalanuvchisi minimal huquqlarga ega.
- Production'da SQL xato matnlari foydalanuvchiga ko'rinmaydi.
- Staging'da sqlmap bilan ehtiyotkor sinov o'tkazilgan va natijalar qo'lda tasdiqlangan.
Xulosa
ORM SQL injection'ni yo'q qilmadi, faqat uni kamroq ko'zga tashlanadigan joylarga surdi. Xavf endi butun kod bo'ylab emas, bir nechta aniq naqshlarda to'plangan: xom so'rovlar, dinamik identifikatorlar, qidiruv va ikkinchi darajali ma'lumotlar. Bu yaxshi xabar — ularni qidirish va yopish oson. Kodni grep bilan ko'rib chiqish, allowlist qo'shish va bazadagi huquqlarni cheklash odatda bir necha soatlik ish.
Loyihangizdagi xom so'rovlar va filtrlarni ekspertizadan oldin tekshirib olmoqchimisiz? Skanerlash uchun ariza qoldiring — Claude AI agenti kod bazangizdagi xavfli naqshlarni tez topadi, mutaxassisimiz esa ularni tasdiqlab, teshiklarni yopishga yordam beradi.


