طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 301 · من الفكرة إلى المنتج (١): تطبيق ويب متكامل
الدرس 6 · الوحدة 3: التأمين وضمان الجودة

المحطّة ٦: التأمين — حقن SQL و XSS والصلاحيات

منتجٌ آمنٌ: يصمد أمام الحقن و XSS ومحاولات الوصول لبيانات الغير. ⏱ نحو 26 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تمنع حقن SQL بالمعاملات المُعدّة في كل استعلام.
  • تمنع XSS بالهروب من كل مخرَجٍ يحوي مدخلات المستخدم.
  • تغلق ثغرة الصلاحيات بربط كل عمليّةٍ بمالكها.
  • تفكّر كمهاجمٍ يرسل الطلبات مباشرةً دون واجهة.

١ثلاث ثغراتٍ يجب أن يصمد أمامها مشروعك

تعلّمت في مقرّر 243 أشهر ثغرات الويب. الآن نطبّقها على منتجنا — فالتأمين ليس محطّةً أخيرة تُضاف، بل عادةٌ في كل سطر. نراجع ثلاثًا أساسيّة:

٢حقن SQL: المُعدّة تغلق الباب

قارن بحثًا هشًّا يدمج النصّ، بآخر مُعدّ. جرّب الهجوم ' OR '1'='1 على الاثنين:

<?phprequire __DIR__ . '/db.php';$q = $_GET['q'] ?? '';// ❌ هشّ: دمج قيمة المستخدم في نصّ SQL$rows = $pdo->query("SELECT title FROM tasks WHERE title = '$q'")->fetchAll();echo "النتائج: " . count($rows);

الاستعلام الهشّ أعاد كل الصفوف — الهجوم نجح.

<?phprequire __DIR__ . '/db.php';$q = $_GET['q'] ?? '';// ✅ مُعدّ: القيمة معاملٌ لا تُفسَّر كـ SQL أبدًا$st = $pdo->prepare('SELECT title FROM tasks WHERE title = ?');$st->execute([$q]);echo "النتائج: " . count($st->fetchAll());

المُعدّ عامل النصّ كلّه قيمةً واحدة — صفر نتائج، الهجوم فشل.

٣XSS: اهرب من كل ما تعرضه

لو أضاف مستخدمٌ مهمّةً عنوانها <script>…</script> وعرضناها كما هي، نُفِّذ السكربت في متصفّح كل من يراها. الدرع: htmlspecialchars يحوّل < و> إلى رموزٍ تُعرَض نصًّا لا تُنفَّذ:

<?php$title = "<b>مهمّة</b><script>alert(1)</script>";echo "بلا هروب: $title\n";echo "مع الهروب: " . htmlspecialchars($title, ENT_QUOTES, 'UTF-8');

٤ثغرة الصلاحيات: لا تثق بالمعرّف وحده

الخطر الأخفى: يرسل المستخدم id مهمّةٍ ليست له. لو كتبنا WHERE id = ? فقط، حذف مهمّة غيره! الدرع: اربط كل عمليّةٍ بالمالك AND user_id = ? من الجلسة — فلا يطال المستخدم إلّا ما يملك:

// ❌ يحذف أيّ مهمّةٍ بالمعرّف — ولو لم تكن له$pdo->prepare('DELETE FROM tasks WHERE id = ?')->execute([$id]); // ✅ يحذف فقط إن كانت المهمّة له$pdo->prepare('DELETE FROM tasks WHERE id = ? AND user_id = ?')->execute([$id, $_SESSION['uid']]);

توقّع وتحقّق

ما الذي يمنع حقن SQL؟

ما الذي يمنع XSS عند عرض عنوانٍ أدخله المستخدم؟

كيف نمنع مستخدمًا من حذف مهمّة غيره؟

لماذا لا يكفي إخفاء زرّ الحذف في الواجهة؟

الخلاصة

تحدٍّ برمجي: تحصين الصفحات

برمجي

حصّن صفحتين من مشروعك: اجعل العرض يهرب من HTML (ضدّ XSS)، واجعل الحذف مقيّدًا بالمالك ومُعدًّا (ضدّ الحقن وثغرة الصلاحيات). سنختبر هجومًا حقيقيًّا بين مستخدمَين.

المطلوب:
  • list.php: يعرض عناوين مهامّ المستخدم بعد تمريرها على htmlspecialchars.
  • delete.php (POST id): يحذف المهمّة فقط إن كانت للمستخدم الحاليّ، باستعلامٍ مُعدّ، ويطبع تمّ.
👀 اعرض الاستجابة المطلوبة
✓ التحقق الآلي: يرسل المختبر هذه الطلبات إلى خادمك بالترتيب ثم يفحص الاستجابات
  1. POST /login.php (username=sarah&password=sarah123)
  2. 👤 ali POST /login.php (username=ali&password=ali123)
  3. POST /add.php (title=<b>اجتماع</b>)
  4. GET /list.php
  5. 👤 ali POST /delete.php (id=1)
  6. GET /list.php
  7. POST /delete.php (id=1)
  8. GET /list.php
  • الكود يعمل بلا أخطاء أو تحذيرات
  • العرض يهرب من HTML (XSS)
  • لا يخرج وسمًا خامًّا قابلًا للتنفيذ
  • مستخدمٌ آخر لا يستطيع حذف مهمّتك
  • المالك يحذف مهمّته هو
  • يستخدم htmlspecialchars في العرض
  • الحذف مقيّدٌ بالمالك user_id

💡 تلميح 1
العرض: echo htmlspecialchars($t["title"], ENT_QUOTES, "UTF-8");.
💡 تلميح 2
الحذف: DELETE FROM tasks WHERE id = ? AND user_id = ? ثم مرّر id والجلسة.
💡 تلميح 3
الهجوم في الاختبار: «ali» يحاول حذف مهمّة «sarah» رقم 1 — يجب أن يفشل، ثم تحذفها sarah نفسها بنجاح.

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

<?phpsession_start();require __DIR__ . '/db.php';if (!isset($_SESSION['uid'])) { echo 'سجّل الدخول'; exit; }$st = $pdo->prepare('SELECT title FROM tasks WHERE user_id = ? ORDER BY id');$st->execute([$_SESSION['uid']]);foreach ($st->fetchAll() as $t) {    echo htmlspecialchars($t['title'], ENT_QUOTES, 'UTF-8') . "\n";}

🐞 لاحظت خطأً في هذا الدرس؟ أبلغني بالبريد

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس