एक सपोर्ट-टिकट API {"title": 7, "description": "<बहुत बड़ा टेक्स्ट>", "email": "alice@example.com"} को बिना रिक्वेस्ट के आकार (shape) की जांच किए स्वीकार कर लेती है। बाद में फॉर्मेटिंग का स्टेप न्यूमेरिक टाइटल पर क्रैश हो जाता है, जबकि एरर हैंडलर सबमिट किए गए डिस्क्रिप्शन और ईमेल को रिकॉर्ड कर लेता है। अटैकर को किसी अकाउंट या एक्सप्लॉइट चेन की ज़रूरत नहीं थी, बस एक ऐसी रिक्वेस्ट चाहिए थी जो API की सीमा को ऐसे डेटा के साथ पार करे जिसे सर्विस भरोसेमंद मानती हो। हमारा सवाल यह है कि सर्विस को गलत फॉर्मेट वाले, बहुत बड़े और अनपेक्षित इनपुट को बिज़नेस लॉजिक या लॉग तक पहुँचने से पहले कहाँ रिजेक्ट करना चाहिए।
हम पूरे कोर्स के दौरान एक FastAPI सर्विस का इस्तेमाल करेंगे। POST /tickets एक नया टिकट स्वीकार करता है, GET /tickets/{id} एक टिकट को पढ़ता है, POST /login यूज़र को ऑथेंटिकेट करता है, और GET /me ऑथेंटिकेटेड यूज़र की जानकारी देता है। हर एंडपॉइंट क्लाइंट द्वारा कंट्रोल किया गया डेटा प्राप्त करता है, भले ही क्लाइंट आज एक भरोसेमंद ब्राउज़र हो, इसलिए हर एंडपॉइंट को प्राप्त इनपुट और इंटरनल स्टेट के बीच एक स्पष्ट सीमा की आवश्यकता होती है।
इनपुट वैलिडेशन वह सीमा नियम है जो केवल सर्विस के कॉन्ट्रैक्ट से मेल खाने वाले डेटा को स्वीकार करता है। POST /tickets के लिए, एक वैलिड रिक्वेस्ट में छोटा टेक्स्ट टाइटल, सीमित लंबाई का डिस्क्रिप्शन और ज़रूरी फॉर्मेट में ईमेल होना चाहिए। गलत फॉर्मेट वाला JSON और भी पहले फेल हो जाता है क्योंकि फ्रेमवर्क गलत सिंटैक्स से रिक्वेस्ट बॉडी नहीं बना पाता है। इसका उपयोगी परिणाम केवल कम एक्सेप्शन (exceptions) होना नहीं है। रिजेक्ट किया गया इनपुट कभी भी टिकट स्टोरेज, रिस्पॉन्स रेंडरिंग या डायग्नोस्टिक लॉग तक नहीं पहुँचता है, जिससे पर्सनल डेटा एक्सपोज़ हो सकता है।
सर्विस में तीन सीमाएँ हैं जिन्हें बनाते समय ध्यान में रखना ज़रूरी है: रिक्वेस्ट पार्सिंग, POST /login पर ऑथेंटिकेशन, और रिस्पॉन्स रेंडरिंग। पहली सीमा हमारा शुरुआती बिंदु है क्योंकि हर अन-ऑथेंटिकेटेड रिक्वेस्ट इसी तक पहुँचती है। आइए सर्विस के ज़रिए कंट्रोल किए जाने वाले फील्ड्स को ट्रेस करें:
यह डायग्राम दिखाता है कि title, description, और email सर्विस के बाहर से आते हैं, जबकि इन-मेमोरी रिपॉजिटरी को सिर्फ़ वही डेटा मिलता है जो रिक्वेस्ट बाउंड्री से गुज़रा हो।
क्योंकि डायग्राम क्लाइंट डेटा को रिपॉजिटरी डेटा से अलग करता है, इसलिए POST /tickets के लिए ऐसे नियमों की ज़रूरत होती है जिन पर हैंडलर भरोसा कर सके, न कि ऐसे अनुमानों पर जो बाद के कोड में बिखरे हों। कॉन्ट्रैक्ट में उन फ़ील्ड्स, उनके प्रकारों और उनकी सीमाओं के नाम बताए जाते हैं जिनकी इजाज़त है। टाइटल में 1 से 120 कैरेक्टर हो सकते हैं, जबकि डिस्क्रिप्शन में 4,000 तक हो सकते हैं। सीमाएँ सुरक्षा कंट्रोल हैं क्योंकि वे स्टोरेज, प्रोसेसिंग, लॉग वॉल्यूम और रिस्पॉन्स साइज़ को सीमित करती हैं, ताकि कोई हमलावर उन्हें बढ़ा न सके।
जब सर्विस को एक कैननिकल फ़ॉर्म की ज़रूरत होती है, तो नॉर्मलाइज़ेशन उसी बाउंड्री में आता है। हम टाइटल को स्टोर करने से पहले उसके आस-पास के व्हाइटस्पेस को हटा सकते हैं, लेकिन हमें किसी गलत ईमेल को चुपचाप ठीक नहीं करना चाहिए या बहुत बड़े डिस्क्रिप्शन को छोटा नहीं करना चाहिए। ठीक करने से क्लाइंट का मतलब बदल जाता है; रिजेक्ट करने से कॉन्ट्रैक्ट साफ़ तौर पर दिखता है। Pydantic फ़ील्ड वैलिडेटर स्वीकार किए गए मानों को बदल सकते हैं, लेकिन वैलिडेटर को स्वीकार किया गया परिणाम देना चाहिए, और जिन नियमों को रॉ इनपुट की ज़रूरत होती है, उन्हें किसी भी प्रकार के इनपुट को ध्यान में रखना चाहिए।
जब email not-an-email हो, तो POST /tickets को क्या रिस्पॉन्स देना चाहिए? हैंडलर को टिकट 42 नहीं बनाना चाहिए, और क्लाइंट को इंटरनल ट्रेसबैक के बजाय एक स्थिर एरर फ़ॉर्मेट की ज़रूरत होती है। नीचे दिया गया रिक्वेस्ट मॉडल सीमित फ़ील्ड्स लागू करता है और रिजेक्ट किए गए ईमेल को रिकॉर्ड किए बिना रिजेक्ट किए गए फ़ील्ड को रिकॉर्ड करता है: