کوڈ کی ایک لائن آپ کے پورے ایکسیس کنٹرول ماڈل کو تباہ کر سکتی ہے۔ اگر آپ ریکوسٹ باڈی (request body) کو براہ راست ڈیٹا بیس اپ ڈیٹ میں شامل کر دیتے ہیں، تو آپ کلائنٹ کے ہاتھ میں اپنے اسکیما (schema) کو دوبارہ لکھنے کے لیے قلم تھما دیتے ہیں۔ اسے 'ماس اسائنمنٹ' (mass assignment) کہتے ہیں۔ یہ کوئی غیر معمولی بگ یا معمولی معاملہ نہیں ہے۔ یہ ڈیزائن کی وہ ناکامی ہے جو اس وقت ظاہر ہوتی ہے جب کوئی API پی لوڈ (payload) کو اپنی اپ ڈیٹ پالیسی کے طور پر استعمال کرتی ہے۔
await db.users.update(req.params.id, { ...req.body });
یہ دیکھنے میں صاف ستھرا لگتا ہے۔ اس سے ٹائپنگ بچتی ہے۔ لیکن کلائنٹ 'کیز' (keys) کو کنٹرول کرتا ہے۔ ایک حملہ آور ایک عام پروفائل اپ ڈیٹ میں "role": "admin", "accountId": "someone_else", یا "credit": 99999" جیسی چیزیں شامل کر سکتا ہے۔ آپ کا ویلیڈیشن لیئر (validation layer) شاید یہ چیک کرے کہ آیا وہ ویلیوز اسٹرنگ (strings) ہیں یا نمبرز (numbers) اور یہ کہہ دے کہ وہ ٹھیک لگ رہی ہیں۔ تاہم، 'درستگی' (validity) کا مطلب 'اجازت' (authorization) نہیں ہے۔ ایک صارف قانونی طور پر مطلوبہ ریکارڈ کا مالک ہو سکتا ہے، لیکن اس کا مطلب یہ نہیں کہ اسے اس کے اندر موجود ہر فیلڈ کو ایڈٹ کرنے کا حق حاصل ہے۔
ماس اسائنمنٹ درحقیقت کیسا نظر آتا ہے
خطرہ سہولت میں چھپا ہوتا ہے۔ فریم ورکس (Frameworks) اور ORMs JSON کیز کو براہ راست ڈیٹا بیس کالمز پر میپ کرنا انتہائی آسان بنا دیتے ہیں۔ جب آپ ایسا کرتے ہیں، تو آپ ڈیٹا بیس کو یہ کہہ رہے ہوتے ہیں کہ وہ کلائنٹ پر بھروسہ کرے کہ کیا تبدیل ہونا چاہیے، نہ کہ صرف یہ کہ اسے کیسے تبدیل ہونا چاہیے۔
اپنا پروفائل اپ ڈیٹ کرنے والا صارف displayName اور bio کے لیے درست ڈیٹا بھیج سکتا ہے، لیکن ان کے ساتھ role یا balance کو بھی شامل کر سکتا ہے۔ اگر آپ کا کنٹرولر (controller) محض اس آبجیکٹ کو آگے بھیج دیتا ہے، تو ڈیٹا بیس سب کچھ لکھ دے گا۔ ویلیڈیشن غلط فارمیٹ والی ویلیوز کو پکڑ لیتی ہے، لیکن یہ شاذ و نادر ہی خطرناک (malicious) کیز کو پکڑ پاتی ہے۔ وہ بزنس رول جو کہتا ہے کہ "اس صارف کو اپنا پروفائل اپ ڈیٹ کرنے کی اجازت ہے"، اس طرح ہر قطار (row) کے ہر کالم پر ایک بلا شرکتِ غیرے اجازت بن جاتا ہے۔
اس کا حل مزید ویلیڈیشن نہیں ہے۔ بلکہ اس کا حل ایک سخت تر آرکیٹیکچر (architecture) ہے۔
تین دروازے
ایک محفوظ تبدیلی (mutation) اسٹوریج کو چھونے سے پہلے تین الگ الگ چیک سے گزرتی ہے۔
قبول شدہ فیلڈز (Allowlist)
اس بات سے آغاز کریں کہ آپ کن کیز (keys) کو دیکھیں گے۔ اگر کوئی فیلڈ allowlist میں نہیں ہے، تو ریکوسٹ کو مسترد کر دیں یا اس کی (key) کو نکال دیں۔ یہ ڈیفالٹ طرزِ عمل کو بدل دیتا ہے: نئے ڈیٹا بیس کالمز تب تک لکھنے کے قابل (non-writable) نہیں ہوتے جب تک کہ کوئی ڈویلپر انہیں واضح طور پر ظاہر نہ کر دے۔ اسکیما وقت کے ساتھ بڑھتے ہیں۔ ایک ساتھی stripeCustomerId ، departmentBudget یا isVerified فلیگ شامل کر سکتا ہے۔ Allowlist کے ساتھ، وہ نئے کالم خود بخود کلائنٹ کی طرف سے لکھے جانے سے محفوظ رہتے ہیں۔ اس کے بغیر، ہر نیا کالم حادثاتی طور پر ایک API سطح (surface) بن جاتا ہے۔
درست ویلیوز (Valid Values)
ایک بار جب آپ کو معلوم ہو جائے کہ کون سی فیلڈز کی اجازت ہے، تو چیک کریں کہ آیا ویلیوز کا کوئی مطلب بنتا ہے۔ کیا ٹائم زون اسٹرنگ (timezone string) واقعی ایک تسلیم شدہ ٹائم زون ہے؟ کیا ای میل کا فارمیٹ ای میل جیسا ہے؟ کیا نمبر ایک مناسب حد (range) کے اندر ہے؟ یہ صفائی ستھرائی (hygiene) کا حصہ ہے۔ یہ کچرا آپ کے سسٹم میں داخل ہونے سے روکتا ہے، لیکن یہ غلط استعمال (abuse) کو نہیں روکتا۔ ایک بالکل درست "admin" اسٹرنگ بھی role فیلڈ میں خطرناک ہو سکتی ہے اگر اسے غلط شخص بھیج دے۔
مجاز تبدیلیاں (Authorized Transitions)
یہ وہ دروازہ ہے جسے زیادہ تر ٹیمیں چھوڑ دیتی ہیں، اور اصل تحفظ یہیں موجود ہے۔ ایک باریک بینی والا سوال پوچھیں: کیا اس مخصوص اداکار (actor) کے پاس اس مخصوص ریکارڈ پر اس مخصوص فیلڈ کو تبدیل کرنے کی اجازت ہے؟ یہ نہیں کہ "کیا صارف ایڈمن ہے؟" یا "کیا صارف کے پاس write:users اسکوپ ہے؟" بلکہ یہ کہ "کیا اس صارف کو اپنا displayName تبدیل کرنے کی اجازت ہے، لیکن کبھی بھی اپنا accountId نہیں؟" فی-فیلڈ اجازت (Per-field authorization) "ایڈیٹر" یا "صارف" جیسی وسیع اجازت کو قطار کی ہر پراپرٹی کے لیے ماسٹر کی (master key) بننے سے روکتی ہے۔
پیچ فنکشن (Patch Function) بنانا
ان تینوں دروازوں کو ایک ہی پائپ لائن (pipeline) میں جوڑ دیں۔ جب کوئی پیچ ریکوسٹ (patch request) آئے، تو اسے ترتیب وار مراحل سے گزاریں۔
سب سے پہلے، ان پٹ کو اپنی allowlist کے خلاف فلٹر کریں۔ اگر role اس اینڈ پوائنٹ (endpoint) کے لیے ایک اجازت شدہ فیلڈ نہیں ہے، تو وہیں رک جائیں۔ ایسی ویلیو کو ویلیڈیٹ یا مجاز کرنے کا کوئی فائدہ نہیں جو آپ کو کبھی موصول ہی نہیں ہونی چاہیے تھی۔
دوسرا، اجازت شدہ ویلیوز کو ویلیڈیٹ کریں۔ ٹائپس (types)، فارمیٹس (formats) اور بزنس رولز کو چیک کریں۔ لوکیشن فیلڈ کو ایک ایسی اسٹرنگ ہونی چاہیے جو ایک حقیقی ٹائم زون کو ظاہر کرے۔ ایواٹار (avatar) URL ایک مخصوص لمبائی کے اندر ایک درست URI ہونا چاہیے۔
تیسرا، عمل کو مجاز (authorize) کریں۔ تصدیق کریں کہ اداکار مطلوبہ ریکارڈ کا مالک ہے، یا اس فیلڈ کے لیے درکار درست اجازت رکھتا ہے۔ ذاتی ڈیٹا کے لیے ملکیت (ownership) ایک اچھا ڈیفالٹ ہے، لیکن کچھ فیلڈز کو اب بھی اضافی دروازوں کی ضرورت ہوتی ہے۔ ایک صارف اپنے پروفائل کا مالک ہو سکتا ہے، لیکن taxRegion کو صرف بلنگ ایڈمن (billing admin) کو ہی چھونا چاہیے۔
چوتھا، ڈیٹا کو نارملائز (normalize) کریں۔ وائٹ سپیس (whitespace) کو ختم کریں، بار بار آنے والے سپیس کو کم کریں، ای میلز کو لوئر کیس (lowercase) کریں، یا کنٹرول کیریکٹرز کو ہٹا دیں۔ یہ کام ویلیڈیشن کے بعد لیکن اسٹوریج سے پہلے کریں تاکہ آپ اتھارائزیشن چیک کے دوران گندی اسٹرنگز (dirty strings) کا موازنہ نہ کر رہے ہوں۔
اگر ان پٹ کسی بھی مرحلے (gate) پر ناکام ہو جائے تو پوری میوٹیشن (mutation) کو مسترد کر دیں۔ محفوظ فیلڈز کو جزوی طور پر لاگو نہ کریں اور خراب فیلڈز کو خاموشی سے نظر انداز نہ کریں۔ ایک مخلوط جواب کلائنٹس کو اس بات کی تربیت دیتا ہے کہ وہ ہر ممکنہ کی (key) بھیجیں اور دیکھیں کہ کون سی قبول ہوتی ہے۔ واضح طور پر ناکامی کا پیغام دیں۔
وہ اہم کیسز (Edge Cases) جو حقیقت میں اہمیت رکھتے ہیں
ماس اسائنمنٹ (Mass assignment) کے دفاع کا دارومدار ان تفصیلات پر ہوتا ہے جنہیں یونٹ ٹیسٹ اکثر نظر انداز کر دیتے ہیں۔
ڈپلیکیٹ JSON کیز (Duplicate JSON keys)۔ حملہ آور {"role": "user", "role": "admin"} جیسے پے لوڈز (payloads) بھیج سکتے ہیں۔ آپ کے HTTP پارسر اور فریم ورک کے لحاظ سے، آپ کے ایپلی کیشن کوڈ کے آبجیکٹ دیکھنے سے پہلے دوسری ویلیو پہلی کو اوور رائٹ (overwrite) کر سکتی ہے۔ اس رویے کا پارسر کی سطح پر ٹیسٹ کریں۔ اگر آپ کا فریم ورک خاموشی سے آخری کی (key) کو قبول کر لیتا ہے، تو ہو سکتا ہے کہ آپ کی allowlist "user" کو دیکھ رہی ہو جبکہ ڈیٹا بیس میں "admin" موصول ہو رہا ہو۔
نیস্টেڈ آبجیکٹس، نل (nulls)، اور ایرے (arrays)۔ یہ فرض نہ کریں کہ پے لوڈ فلیٹ (flat) ہے۔ ایک کلائنٹ کسی محدود فیلڈ کو نیস্টেڈ آبجیکٹ کے اندر لپیٹ سکتا ہے جیسے کہ { "profile": { "role": "admin" } }۔ اگر آپ کا اسکیما (schema) ریکرس (recurse) کرتا ہے تو آپ کی allowlist کو بھی کرنا چاہیے۔ اسی طرح، یہ فیصلہ کریں کہ آپ null کو کیسے ہینڈل کرتے ہیں۔ کیا اس کا مطلب "اس فیلڈ کو نظر انداز کریں" ہے یا "اس فیلڈ کو حذف کریں"؟ اور اگر ایک ایرے کی توقع ہو، تو کیا آپ کا ویلیڈیٹر غیر متوقع ڈھانچوں کو مسترد کر دیتا ہے، یا وہ ایک سنگل آبجیکٹ کو ایرے میں تبدیل کر کے اسے گزرنے دیتا ہے؟
یونیکوڈ نارملائزیشن (Unicode normalization)۔ دو اسٹرنگز (strings) انسان کو ایک جیسی نظر آ سکتی ہیں جبکہ وہ بائٹس (bytes) کے مختلف تسلسل ہو سکتے ہیں۔ ایک صارف پہلے سے تیار شدہ é یا ڈی کمپوزڈ (decomposed) e اور اس کے ساتھ ایک ایکسینٹ (accent) بھیج سکتا ہے۔ اگر آپ کا اتھارائزیشن چیک ایک بار نارملائز کرتا ہے لیکن آپ کا اسٹوریج لیئر مختلف طریقے سے نارملائز کرتا ہے، تو آپ غیر مستقل ڈیٹا کے ساتھ یا اس سے بھی بدتر، ایک بائی پاس (bypass) کے ساتھ ختم ہو سکتے ہیں جہاں یوزر نیم کا ٹکراؤ آپ کے لاجک سے بچ نکلتا ہے۔ شروع میں ہی اور مستقل مزاجی سے نارملائز کریں۔
ریس کنڈیشنز (Race conditions)۔ اتھارائزیشن کے فیصلے ساکت (freeze frames) نہیں ہوتے۔ وہ ایک مخصوص وقت پر ہوتے ہیں۔ دو درخواستیں ایک ہی ریکارڈ پڑھ سکتی ہیں، دونوں دیکھ سکتی ہیں کہ ایکٹر (actor) کو لکھنے کی اجازت ہے، اور دونوں اپ ڈیٹس جاری کر سکتی ہیں۔ اس دوران، اسٹیٹ (state) یا ایکٹر کی اجازتیں تبدیل ہو سکتی ہیں۔ ہمیشہ ورژن نمبر یا اسٹیٹ مشین ویلیو پر شرط کے ساتھ ڈیٹا بیس اپ ڈیٹس لاگو کریں۔ کچھ ایسا استعمال کریں جیسے UPDATE users SET ... WHERE id = ? AND version = 5۔ اگر آپ کے پڑھنے کے بعد سے رو (row) تبدیل ہو گئی ہے، تو رائٹ (write) ناکام ہو جائے گی۔ ناکامی کو دوبارہ کوشش (retry) کر کے یا مسترد کر کے ہینڈل کریں۔ یہ پرانی اتھارائزیشن چیکس کو آپ کے ڈیٹا کو خراب کرنے سے روکتا ہے۔
اس چیز کی نگرانی کریں جو اہم ہے
آپ اس چیز کو محفوظ نہیں کر سکتے جسے آپ دیکھ نہیں سکتے۔ اپنی آڈٹ لاگنگ (audit logging) کو صرف ایکشن کے گرد نہیں بلکہ فیصلے کے گرد بنائیں۔
ایکٹر آئی ڈی (actor ID) اور ٹارگٹ آئی ڈی (target ID) کو لاگ کریں۔ ان عین فیلڈ کے ناموں کو لاگ کریں جو قبول کیے گئے اور وہ جو مسترد کر دیے گئے۔ اس پالیسی ورژن کو لاگ کریں جس نے فیصلہ کیا اور حتمی نتیجہ۔ اگر کوئی صارف اچانک اپنی پروفائل اپ ڈیٹ میں role کو مسترد ہوتے ہوئے دیکھتا ہے، تو آپ فوری طور پر جاننا چاہیں گے۔
بیئرر ٹوکنز (bearer tokens) کو کبھی لاگ نہ کریں۔ اپنے لاگز میں مکمل ریکویسٹ باڈیز (request bodies) کو کبھی نہ ڈالیں۔ ایک آڈٹ ٹریل (audit trail) کو بدسلوکی کی تحقیقات میں آپ کی مدد کرنی چاہیے، نہ کہ یہ کریڈنشلز اور ذاتی ڈیٹا کا ذخیرہ بن جائے۔
وہ واحد اصول جس کی آپ کو ضرورت ہے
ایک ریکویسٹ باڈی ڈیٹا کی تجویز دیتی ہے۔ یہ کبھی اپنی اتھارٹی خود متعین نہیں کرتی۔ کلائنٹ کچھ بھی مانگ سکتا ہے۔ آپ کا سرور، فیلڈ بہ فیلڈ اور رو بہ رو، فیصلہ کرتا ہے کہ مستقل اسٹوریج میں کیا آنے کی اجازت ہے۔ اپنے پیچز (patches) کو اس علیحدگی کو ذہن میں رکھتے ہوئے بنائیں، اور ماس اسائنمنٹ ایک ایسا مسئلہ بن جائے گا جسے آپ اتھارائزیشن لیئر تک پہنچنے سے بہت پہلے ہی روک چکے ہوں گے۔
