نسجّل الدخول إلى وحدة تحكم المسؤولين الخاصة بنا عبر SAML الخاص بنا. وإليكم ما كشفه هذا.
هناك نسخة من "تناول طعامك الخاص" تكون مجرد شعار، ونسخة أخرى لا يستطيع فيها موظفوك أنفسهم نشر أي شيفرة حتى يُصلَح الخطأ. نحن نمارس النسخة الثانية. وحدة تحكم موظفي Authagonal، وهي ذاتها التي نستخدمها لإدارة كل مستأجر، تتم المصادقة عليها من خلال Authagonal نفسه: تسجيل دخول موحّد SAML من دليل Entra الخاص بنا، مع تكفّل SCIM بتحديد من يدخل وما الذي يُسمح له بفعله. لا يوجد جدول منفصل لكلمات مرور المسؤولين. لقد حذفناه. فإذا تعطّل SAML الخاص بنا، أُغلق علينا بابُ منتجنا نفسه.
وهذا أمر غير مريح، لكنه غير مريح بالطريقة المفيدة تماماً. إنه يحوّل عبارة "SSO ميزة مؤسسية ندعمها" إلى عبارة "SSO هو الطريقة الوحيدة التي يستطيع من بنوا هذا أن يعملوا بها اليوم". وإليكم ما كشفه كوننا عميل أنفسنا.
بناء مُشذَّب أعطب التحقق من التوقيع
ننشر خادم المصادقة في صورة مشذَّبة (trimmed)، للإبقاء على حجم الصورة صغيراً. والتشذيب يحذف بقوة الشيفرة التي لا يستطيع إثبات استخدامها، والانعكاس (reflection) يُخفي عنه ذلك الاستخدام. تحلّ .NET خوارزميات التوقيع بصيغة XML المعتمِدة على التشفير بالاسم، انعكاسياً، عبر CryptoConfig. لم يستطع أداة التشذيب رؤية أن هذه الأنواع لازمة، فأزالها، فعاد SignedXml بهدوء عاجزاً عن بناء الخوارزمية. والتحقق من توقيع SAML، أي الخطوة التي تثبت أن تسجيل الدخول حقيقي، رمى مرجعاً فارغاً (null reference) عند التشغيل.
نجحت اختبارات الوحدة، لأنها جرت على البناء غير المشذَّب حيث كانت الأنواع لا تزال موجودة. وحده النتاج الإنتاجي المشذَّب هو الذي فشل، وفشل في اللحظة التي حاول فيها إنسان تسجيل الدخول بالضبط. أصبحنا الآن ننشر خادم المصادقة غير مشذَّب، مع ارتياب صحّي تجاه تشذيب أي شيء قريب من التشفير المعتمِد على الانعكاس. ولو كنا ندعم SAML فحسب بدلاً من أن نعيش عليه، لكان هذا تقريرَ حادثٍ من أحد العملاء بدلاً من أن يكون تقريرَنا نحن.
التزويد هو تسجيل الدخول الفعلي
مصادقة المستخدم هي النصف السهل من SSO. أما النصف الصعب فهو تحديد ما يُسمح له بفعله وإبقاء ذلك متزامناً مع انضمام الناس ومغادرتهم. نحن ندير ذلك عبر SCIM: تنعكس عضوية المجموعة في Entra على الأدوار، ويُحسَم ذلك في لحظة إصدار الرمز، لا أن يُنسَخ مرة واحدة عند إنشاء الحساب. أضِف شخصاً إلى المجموعة الصحيحة فيحصل على الوصول عند تسجيل دخوله التالي؛ وأزِله فيختفي الوصول. قائمة وصولنا نفسها تتناول الخريطة ذاتها من المجموعة إلى الدور التي نطرحها.
هذا التوصيل يكشف صنفاً من الأخطاء لا يوجد إلا حين تعيش المصادقة والتفويض في نظامين مختلفين: قد يُصادَق على مسؤولٍ جرى تزويده للتو قبل أن يكتمل وصول دوره، فيترك تسجيل الدخول الأول في حالة "صالحة لكن غير مفوَّضة". الإصلاح يكمن في ترتيب التزويد، لا في تسجيل الدخول، ولا تكتشفه إلا بأن تكون أنت المسؤول الجديد الذي يسجّل الدخول لأول مرة.
خطأ 500 كان يجب أن يكون 403
أصغر الأخطاء كان أكثرها إحراجاً. حين يصل مستخدم مُصادَق عليه إلى شيء غير مسموح له به، كانت الـ API تُعيد خطأ 500 بدلاً من 403 نظيف، لأن مسار الشيفرة الذي يُصدر استجابة "ممنوع" كان يعتمد على خدمة لم تُوصَّل في ذلك المُضيف. من المفترض أن يكون الطلب المرفوض نتيجة هادئة ومتوقَّعة، لا خطأَ خادم. غير مرئيٍّ حتى تكون أنت من يُرفَض.
الحدّ الذي يهمنا أكثر من غيره
أسفل كل ذلك تقع القاعدة الوحيدة التي لا يجوز لمنصة هوية متعددة المستأجرين أن تخطئ فيها: يجب ألّا يتحوّل أي مستأجر إلى مسؤول لدينا. نحن لا نثق بفحص واحد. المنصة مُصدِّر (issuer) قائم بذاته ولا يستطيع مستأجر أن يدّعي ملكية معرّفها المختصر (slug)؛ ومفتاح توقيع المنصة منفصل عن مفتاح كل مستأجر، فلا يمكن إعادة تشغيل رمز موقَّع لمستأجرٍ بوصفه رمز منصة؛ ومخزن المنصة يفرض أدوار المنصة بشكل مستقل. ثلاثة أقفال، لأن كلفة فتح أحدها على مصراعيه هي المنتج بأكمله.
الخلاصة
لم يُكتشَف أيٌّ من هذه عبر اختبار بارع. اكتُشفت عبر شخص يحاول أداء عمله فلا يستطيع. هذه هي الحجة لاستخدام منتجك الخاص بالعمق الذي يمكن أن يؤذيك عنده: فهو يحوّل الأخطاء المختبئة في الفجوات بين الأنظمة إلى أخطاء تُصلحها قبل الإفطار، لأنك لا تستطيع النشر حتى تفعل.
كل ما تتكئ عليه وحدة تحكمنا (SSO وSAML وSCIM وMFA وسجلات التدقيق) مُضمَّن في كل خطة من خطط Authagonal، وليس محجوزاً خلف طبقة سعرية أو محسوباً على أساس كل اتصال. اطّلع على ما هو مُضمَّن.