الـ Input · فصل ١

SQL Injection — لما الـ input يبقى كود

SQL Injection · parameterized queries · prepared statements · ORM raw queries
🎯 الهدف: تفهم إزاي string concatenation بيحوّل الـ input لجزء من الـ query، وتقفلها بالـ parameterized queries في كل مكان.

الفكرة في سطر

الـ database مش بتعرف أنهي جزء من الـ query إنت كاتبه وأنهي جزء المستخدم كاتبه. لو لزقت الـ input جوه الـ string، المستخدم يقدر يكتب SQL مش اسم.

الكود اللي فيه الثغرة

users.js
// ❌ الـ input اتلزق في الـ queryapp.get("/users", async (req, res) => {  const name = req.query.name;  const sql = `SELECT * FROM users WHERE name = '${name}'`;  const { rows } = await pool.query(sql);  res.json(rows);});

لو name قيمته ' OR '1'='1، الـ query بتبقى:

SELECT * FROM users WHERE name = '' OR '1'='1'

الشرط بقى صح دايمًا، فرجعت كل المستخدمين. ونفس الفكرة ممكن تتمد لقراية جداول تانية أو تعديل داتا، على حسب صلاحيات الـ database user.

الحل: parameterized queries

الـ input داتا، عمره ما يبقى جزء من نص الـ SQL. بتبعت الـ query بـ placeholders، والقيم لوحدها، والـ database نفسها هي اللي بتفصل بينهم.

users.js
app.get("/users", async (req, res) => {  const sql = `SELECT * FROM users WHERE name = '${req.query.name}'`;  const sql = "SELECT * FROM users WHERE name = $1";  const { rows } = await pool.query(sql, [req.query.name]);  res.json(rows);});

دلوقتي لو حد بعت ' OR '1'='1، الـ database هتدوّر على مستخدم اسمه حرفيًا كده، ومش هتلاقي.

الـ ORM مش بيحميك في كل حتة

Prisma و TypeORM و Sequelize بيعملوا parameters لوحدهم في الـ API العادي. المشكلة في الـ raw queries:

prisma.ts
// ✅ tagged template: Prisma بيحوّل ${} لـ parametersawait prisma.$queryRaw`SELECT * FROM "User" WHERE email = ${email}`;// ❌ Unsafe: إنت اللي بتبني الـ stringawait prisma.$queryRawUnsafe(`SELECT * FROM "User" WHERE email = '${email}'`);
sort.js
const SORTS = { newest: "created_at DESC", name: "name ASC" };const orderBy = SORTS[req.query.sort] ?? SORTS.newest;const sql = `SELECT * FROM jobs ORDER BY ${orderBy} LIMIT $1`;

طبقات زيادة

🔐 أقل صلاحيات

الـ database user بتاع التطبيق ميبقاش owner ولا superuser. يقرا ويكتب في الجداول اللي محتاجها بس.

✅ Validation

zod أو class-validator على كل input: نوعه وطوله وشكله. مش بديل عن الـ parameters، بس بيقلل السطح.

خلّصت الفصل لما

🧠 اختبر نفسك
١) عملت escape للـ single quotes بإيدك. كده أمان؟
مش مضمون. الـ escaping اليدوي بيفوّت حالات. الـ parameterized query هي الحل الأساسي.
٢) ليه الـ ORM مش ضمان كامل؟
عشان الـ raw queries ومكان زي ORDER BY ممكن ترجّع الـ concatenation تاني.
٣) المستخدم بيختار اسم العمود اللي يرتب بيه. تعمل إيه؟
allow-list: الاسم يتحوّل لقيمة ثابتة من خريطة، وأي حاجة تانية ترفضها.