تقبل واجهة برمجة تطبيقات (API) خاصة بتذاكر الدعم طلباً بصيغة {"title": 7, "description": "<very large text>", "email": "alice@example.com"} دون التحقق من هيكلية الطلب. تتسبب خطوة لاحقة لتنسيق البيانات في تعطل النظام بسبب العنوان الرقمي، بينما يقوم معالج الأخطاء بتسجيل الوصف والبريد الإلكتروني المُرسَلين. لم يحتج المهاجم إلى حساب أو سلسلة استغلال معقدة، بل اكتفى بإرسال طلب عبر حدود واجهة برمجة التطبيقات يحتوي على بيانات تعاملت معها الخدمة على أنها موثوقة. يكمن سؤالنا في تحديد النقطة التي ينبغي للخدمة عندها رفض المدخلات غير السليمة أو الضخمة أو غير المتوقعة، وذلك قبل وصولها إلى منطق العمل (business logic) أو سجلات النظام.
سنعتمد خدمة واحدة مبنية بإطار عمل FastAPI طوال فترة الدورة التدريبية. تتعامل المسارات (endpoints) كالتالي: POST /tickets لقبول تذكرة جديدة، وGET /tickets/{id} لقراءة تذكرة محددة، وPOST /login لمصادقة المستخدم، وGET /me لإرجاع بيانات المستخدم المصادق عليه. تتلقى كل نقطة نهاية بيانات يتحكم فيها العميل (client) - حتى لو كان العميل متصفحاً موثوقاً - ولذا تتطلب كل منها وجود حد فاصل صريح بين المدخلات المستلمة والحالة الداخلية للخدمة.
تُعد عملية التحقق من صحة المدخلات بمثابة قاعدة حدودية لا تقبل إلا البيانات التي تتوافق مع “عقد الخدمة” (service contract). فبالنسبة للمسار POST /tickets، يجب أن يتضمن الطلب الصحيح عنواناً نصياً قصيراً، ووصفاً محدود الحجم، وبريداً إلكترونياً بالتنسيق المطلوب. أما بيانات JSON غير السليمة فتفشل في مرحلة مبكرة جداً، إذ يعجز إطار العمل عن بناء جسم الطلب (request body) نظراً لعدم صحة الصيغة البرمجية. ولا تقتصر الفائدة المرجوة على تقليل عدد الاستثناءات (exceptions) فحسب؛ بل تضمن أيضاً عدم وصول المدخلات المرفوضة إلى نظام تخزين التذاكر، أو مرحلة إعداد الاستجابة، أو السجلات التشخيصية التي قد تؤدي إلى كشف بيانات شخصية.
تتضمن الخدمة ثلاثة حدود فاصلة ينبغي مراعاتها بوضوح أثناء عملية التطوير: تحليل الطلب (request parsing)، والمصادقة عند المسار POST /login، وإعداد الاستجابة (response rendering). ويُعد الحد الأول نقطة انطلاقنا، نظراً لأن كل طلب - سواء كان مصادقاً عليه أم لا - يمر عبره. لنقم بتتبع الحقول التي يتحكم فيها العميل أثناء مرورها عبر الخدمة:
يوضح هذا الرسم البياني أن الحقول title (العنوان) وdescription (الوصف) وemail (البريد الإلكتروني) ترد من خارج الخدمة، بينما لا يستقبل المستودع الموجود في الذاكرة (in-memory repository) إلا البيانات التي اجتازت حدود الطلب.
نظراً لأن الرسم البياني يفصل بيانات العميل عن بيانات المستودع، فإن المسار POST /tickets يحتاج إلى قواعد يمكن للمعالج (handler) الاعتماد عليها بدلاً من افتراضات متناثرة في أجزاء لاحقة من الكود. يحدد العقد الحقول المسموح بها وأنواعها وقيودها؛ فقد يسمح العنوان بطول يتراوح بين حرف واحد و120 حرفاً، بينما قد يسمح الوصف بما يصل إلى 4000 حرف. وتُعد هذه القيود ضوابط أمنية لأنها تحد من متطلبات التخزين والمعالجة وحجم السجلات (logs) وحجم الاستجابة، مما يمنع المهاجم من تضخيم هذه الموارد.
تندرج عملية “التسوية” (normalization) ضمن الحدود نفسها عندما تتطلب الخدمة صيغة قياسية موحدة. يمكننا إزالة المسافات البيضاء المحيطة بالعنوان قبل تخزينه، ولكن لا ينبغي لنا إصلاح بريد إلكتروني غير صالح أو اقتطاع وصف يتجاوز الحد المسموح به بشكل صامت؛ فالإصلاح يغير المعنى المقصود من العميل، بينما يجعل الرفضُ العقدَ واضحاً وقابلاً للتحقق. يمكن لمُحققّات الحقول في مكتبة Pydantic تحويل القيم المقبولة، ولكن يجب أن تعيد هذه المُحققّات النتيجة المقبولة، كما يجب أن تراعي القواعد التي تتطلب مدخلات خام (raw input) احتمالية ورود مدخلات من أي نوع.
ما الذي يجب أن يعيده المسار POST /tickets عندما تكون قيمة email هي not-an-email (أي نص لا يمثل بريداً إلكترونياً)؟ لا ينبغي للمعالج إنشاء التذكرة رقم 42، كما يحتاج العميل إلى صيغة ثابتة للخطأ بدلاً من تتبع المكدس (traceback) الداخلي. يطبق نموذج الطلب التالي قيوداً على الحقول ويسجل الحقل المرفوض دون تسجيل قيمة البريد الإلكتروني المرفوضة نفسها: