اختبار تطبيقات الويب في المختبر: الوكيل والصلاحيات والحقن
درس 2 من 3
2اختبار تطبيقات الويب في المختبر: الوكيل والصلاحيات والحقن
الصورة الكبيرةالمتصفح يُظهر لك الأزرار التي يسمح لك التطبيق بضغطها. لكن الخادم لا يرى الأزرار، يرى طلبات، وأي طلب يمكن تعديله. اختبار الويب أن تقف بين المتصفح والخادم، وترى كل طلب، وتسأل: «ماذا لو غيّرت هذا الرقم؟ ماذا لو أرسلت هذا الطلب بحساب آخر؟». هكذا وُجدت أغلب الثغرات التي سرّبت ملايين السجلات.
ماذا تشاهد في الرسم: الوكيل يلتقط كل طلب قبل أن يصل للخادم، فتقرؤه وتعدله وتعيد إرساله، وتقارن الرد. كل ثغرة ويب تقريبًا تُكتشف بهذه الدائرة.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
تخيّل مطعمًا تطلب فيه بورقة يحملها النادل إلى المطبخ. الواجهة (المتصفح) تعطيك قائمة بأطباقك فقط. لكن ماذا لو أوقفت النادل وكتبت على الورقة «وأيضًا طلب الطاولة 7» (IDOR)؟ أو كتبت «وأعطني مفتاح المخزن» (الحقن)؟ المطبخ الجيد يتحقق من كل ورقة: هل هذه طاولتك؟ هل هذا طبق في القائمة؟ الوكيل هو أن توقف النادل وتقرأ الورقة.
أدوات: Burp Suite Community أو ZAP كوكيل اعتراض بشهادة مثبتة في متصفح المختبر. الخطوات: خريطة التطبيق (كل صفحة ونقطة API ومعامل)، ثم المصادقة (قفل الحساب، وإعادة تعيين كلمة المرور، وMFA)، ثم الجلسات (أعلام Secure وHttpOnly وSameSite، وانتهاء الجلسة)، ثم الصلاحيات بحسابين وبدورين: كرّر طلب A بجلسة B (IDOR أفقي) وطلب المدير بجلسة مستخدم (رأسي). الحقن (SQL والأوامر والقوالب): الحل الاستعلام المعلّم (parameterised) الذي يفصل البيانات عن الأمر. SSRF: الخادم يجلب رابطًا تتحكم به، فتجعله يطلب 169.254.169.254 (بيانات السحابة الوصفية) أو خدمات داخلية؛ الحل قائمة سماح وIMDSv2. OWASP Top 10:2025 يبدأ بـ A01 Broken Access Control. التدريب: PortSwigger Web Security Academy وOWASP Juice Shop.
قبل أن تبدأ
🌐أمن تطبيقات الويب
تعرف OWASP Top 10:2025.
🎯منهجية الاختبار
الإذن والنطاق والتقرير.
الفكرة الجوهرية
1الوكيل وخريطة التطبيق
ثبّت الوكيل وشهادته في متصفح مخصص للمختبر فقط. تصفح التطبيق كمستخدم عادي، وكل نقرة تُسجّل طلبًا. ثم اقرأ الخريطة: كل نقطة API ومعاملاتها، وأي المعاملات أرقام تعريف (id, user, order)، وأيها يدخل في استعلام أو رابط أو ملف. هذه قائمة اختبارك.
2الصلاحيات: حسابان دائمًا
أخطر ثغرات الويب وأكثرها انتشارًا لا تحتاج أي حيلة: الخادم يسأل «من أنت؟» لكنه ينسى أن يسأل «هل هذا لك؟». الاختبار بسيط ومنظم: أنشئ حسابين A وB (ودور مدير إن وُجد)، والتقط طلبات A، وأعد إرسالها بجلسة B. إن رأى B بيانات A فهذه IDOR. والإصلاح دائمًا في الخادم: تحقق من الملكية لكل كائن، وارفض افتراضيًا.
هذا هو اختبار الحسابين مصغّرًا: bob يطلب طلب alice. في النسخة المكسورة يراه، وفي المصلحة يأخذ 403.
orders = {501: {"owner": "alice", "total": 420}, 502: {"owner": "bob", "total": 99}}
roles = {"alice": "user", "bob": "user", "sara": "admin"}
def get_order_broken(session_user, order_id):
return orders.get(order_id) # authenticated, but never asks "is it yours?"
def get_order_fixed(session_user, order_id):
order = orders.get(order_id)
if order is None:
return 404
if order["owner"] != session_user and roles.get(session_user) != "admin":
return 403 # deny by default
return order
for fn in (get_order_broken, get_order_fixed):
print(fn.__name__)
for user, oid in (("alice", 501), ("bob", 501), ("sara", 501), ("bob", 999)):
print(f" {user:<5} asks for order {oid}: {fn(user, oid)}")3الحقن: البيانات ليست أوامر
الحقن يحدث حين يُلصق نص المستخدم داخل أمر (استعلام SQL، أو أمر نظام، أو قالب)، فيصبح جزءًا من الأمر. الحل ليس «تنظيف» المدخلات بحذف علامات التنصيص، فهناك دائمًا طريقة التفاف، بل الاستعلام المعلّم: الأمر يُرسل وحده، والبيانات تُرسل منفصلة، فلا يمكن أن تتحول إلى أمر مهما كتب المستخدم.
في النسخة المكسورة صار نص المستخدم جزءًا من الشرط فظهر الجميع. في المصلحة بقي نصًا يُبحث عنه حرفيًا فلم يطابق شيئًا.
import sqlite3
db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE staff (name TEXT, salary INT, dept TEXT)")
db.executemany("INSERT INTO staff VALUES (?, ?, ?)",
[("Huda", 18000, "HR"), ("Omar", 22000, "IT"), ("CEO", 95000, "Board")])
def search_broken(dept):
return db.execute(f"SELECT name, salary FROM staff WHERE dept = '{dept}'").fetchall()
def search_fixed(dept):
return db.execute("SELECT name, salary FROM staff WHERE dept = ?", (dept,)).fetchall()
typed = "HR' OR '1'='1" # the classic lab test string
print("broken, normal :", search_broken("HR"))
print("broken, crafted:", search_broken(typed))
print("fixed, crafted:", search_fixed(typed))4SSRF ومنطق العمل
SSRF: ميزة مثل «أدخل رابط صورتك ليجلبها الخادم». إن لم يقيّد الخادم الوجهة، يطلب ما تريد من داخل الشبكة: لوحات إدارة داخلية، أو عنوان البيانات الوصفية للسحابة 169.254.169.254 الذي قد يعطي مفاتيح مؤقتة. الإصلاح: قائمة سماح بالنطاقات، وحجب العناوين الداخلية بعد حل DNS، وIMDSv2 في AWS. ومنطق العمل ثغرات لا تجدها أي أداة: كوبون يُستخدم مرتين، أو كمية سالبة في السلة، أو تخطي خطوة الدفع. تجدها بأن تسأل «ماذا لو فعلت الخطوات بترتيب مختلف؟».
التحقق بعد حل الاسم مهم: نطاق مسموح قد يشير مؤقتًا إلى عنوان داخلي.
import ipaddress
from urllib.parse import urlparse
ALLOWED_HOSTS = {"images.partner-cdn.com"}
def safe_to_fetch(url, resolved_ip):
host = urlparse(url).hostname or ""
if urlparse(url).scheme != "https" or host not in ALLOWED_HOSTS:
return "refused: host not on the allow-list"
ip = ipaddress.ip_address(resolved_ip)
if ip.is_private or ip.is_loopback or ip.is_link_local:
return "refused: resolves to an internal address"
return "ok"
print(safe_to_fetch("https://images.partner-cdn.com/a.png", "151.101.2.10"))
print(safe_to_fetch("http://169.254.169.254/latest/meta-data/", "169.254.169.254"))
print(safe_to_fetch("https://images.partner-cdn.com/a.png", "10.0.3.7")) # DNS points insideمثال محلول
المسألة: في مختبر Juice Shop، تريد خطة اختبار منظمة لصلاحيات التطبيق في ساعتين. اكتبها.
الحل: (1) شغّل Juice Shop محليًا في Docker، ووجّه متصفح المختبر عبر Burp أو ZAP. (2) أنشئ حسابين A وB. (3) بحساب A تصفح كل ما يخص المستخدم: السلة، والطلبات، والعناوين، والتقييمات، واحفظ الطلبات. (4) في Repeater أعد كل طلب فيه رقم تعريف بجلسة B: هل يرى B سلة A؟ (5) جرّب نقاط الإدارة من خريطة التطبيق بجلسة مستخدم عادي. (6) لكل نجاح: الدليل، والأثر، والإصلاح (تحقق ملكية في الخادم)، واختبار آلي يفشل إن عادت. (7) اكتب ملخصًا من صفحة لما وجدته.
خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| HTTPS يحمي التطبيق من الاختراق. | HTTPS يحمي الطريق فقط. الطلب المعدّل يصل مشفرًا وسليمًا إلى خادم لا يتحقق منه. |
| تنظيف المدخلات يكفي لمنع الحقن. | دائمًا توجد طريقة التفاف. الاستعلام المعلّم يمنعه من الأساس. |
| الزر المخفي لا يمكن الوصول إليه. | الخادم يرى الطلبات لا الأزرار، وأي أحد يستطيع إرسال الطلب. |
أين تقابله في العمل
🧑💻مراجعة الكود
المطوّر الذي اختبر الصلاحيات بنفسه يكتب كودًا يتحقق من الملكية تلقائيًا.
🐞مكافآت الثغرات
أغلب المكافآت الكبيرة في السنوات الأخيرة ثغرات صلاحيات.
🏦التطبيقات المالية
اختبار منطق العمل يمنع تحويلًا مرتين أو مبلغًا سالبًا.
اختبر فهمك
ما أول ثغرة في OWASP Top 10:2025؟
كسر التحكم بالوصول بقي الأول لأنه الأكثر انتشارًا وأثرًا.
كيف تختبر IDOR؟
إن رأى B بيانات A فالخادم لا يتحقق من الملكية.
ما الإصلاح الصحيح لحقن SQL؟
فصل البيانات عن الأمر يمنع تحولها إلى أوامر.
لماذا يطلب المختبِر 169.254.169.254 عند اختبار SSRF في السحابة؟
الحماية: قائمة سماح، وحجب العناوين الداخلية، وIMDSv2.
أين يجب أن يكون فحص الصلاحية؟
كل ما في المتصفح يمكن تعديله أو تجاوزه.
بطاقات مراجعة سريعة
تحقق من فهمك
لماذا تحتاج حسابين لاختبار الصلاحيات؟
لأن السؤال «هل يرى B ما يخص A؟» لا يُختبر إلا بهويتين مختلفتين.
ما الخطأ في إصلاح الحقن بقائمة حروف ممنوعة؟
القوائم تُتجاوز بترميزات وطرق أخرى؛ الحل الجذري فصل البيانات عن الأمر.
كيف تمنع عودة ثغرة أصلحتها؟
باختبار آلي في خط CI يفشل إن عاد السلوك القديم.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
أدوات2
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.