تبدو بعض التعبيرات النمطية بريئة تمامًا حتى تواجه مدخلًا معيّنًا، فتتجمّد فجأة وتستهلك المعالج بالكامل. هذه الظاهرة، المعروفة بالتراجع الكارثي، ليست خطأ نادرًا بل ثغرة حقيقية يمكن استغلالها لحجب الخدمة. سببها أن المحرّك، حين يواجه نمطًا ملتبسًا ومدخلًا عدائيًّا، قد يستكشف عددًا هائلًا من مسارات المطابقة قبل أن يستسلم. فهم كيف ينشأ هذا الالتباس هو السبيل لكتابة أنماط آمنة وسريعة.
كيف يطابق المحرّك ويتراجع
تعمل كثير من محرّكات regex بأسلوب التجريب والتراجع. فحين يصل المحرّك إلى موضع يمكن مطابقته بأكثر من طريقة، يجرّب أحد الخيارات، فإن فشل لاحقًا عاد وجرّب غيره. هذا التراجع طبيعيّ ومفيد في الحالات العادية، ويتيح للأنماط المرنة أن تعمل.
المشكلة تنشأ حين يتضاعف عدد الخيارات الممكنة بشكل انفجاري. فإذا كان كل موضع يقبل عدّة تفسيرات، وتداخلت هذه المواضع، صار عدد المسارات التي يجب تجريبها هائلًا. عندئذٍ يقضي المحرّك وقتًا يتضاعف مع طول المدخل قبل أن يحسم.
الالتباس هو الجذر
جذر التراجع الكارثي هو الالتباس: حين يستطيع المحرّك تقسيم المدخل بين أجزاء النمط بطرق كثيرة. أوضح أمثلته هو التكرار المتداخل، حيث يقع جزء قابل للتكرار داخل جزء آخر قابل للتكرار. عندئذٍ توجد طرق لا تُحصى لتوزيع الأحرف بين المستويين.
ما دامت المطابقة تنجح، قد لا يظهر المشكل. لكن حين يكون المدخل قريبًا من المطابقة ثم يفشل في النهاية، يضطرّ المحرّك إلى تجريب كل التوزيعات الممكنة قبل أن يعلن الفشل. هذا المدخل "القريب من المطابقة لكن الفاشل" هو بالضبط ما يصنعه المهاجم.
المدخل العدائي يستغلّ الضعف
الخطورة أن المهاجم لا يحتاج إلى وصول خاصّ. فمجرّد إرسال نص مصمَّم بعناية إلى نظام يطبّق نمطًا ضعيفًا قد يجعل المعالج يدور لثوانٍ أو دقائق على مدخل صغير. هذا هو جوهر هجوم حجب الخدمة عبر التعبيرات النمطية.
ما يجعل هذا الهجوم خبيثًا هو عدم تناسبه. فمدخل بحجم بضع عشرات من الأحرف قد يولّد عملًا حسابيًّا يتجاوز كل الحدود المعقولة. ولأن الأنماط الضعيفة كثيرًا ما تطابق المدخلات العادية بلا مشكلة، تبقى الثغرة كامنة حتى تُستغَلّ.
كيف تتعرّف على الأنماط الخطرة
أوضح علامة خطر هي التكرار المتداخل: كمّية مطبّقة على مجموعة تحتوي بدورها على كمّية. علامة أخرى هي البدائل المتداخلة التي تقبل المدخل نفسه بطرق متعدّدة. حين ترى نمطًا يمكن أن يطابق جزءًا من النص بأكثر من توزيع، فلتثر شكوكك.
الاختبار العمليّ هو تجريب النمط على مدخلات طويلة من حرف متكرّر تنتهي بحرف لا يطابق. إن تباطأ الأداء بشكل غير متناسب مع طول المدخل، فالنمط معرّض للتراجع الكارثي. هذا الاختبار البسيط يكشف كثيرًا من الأنماط الخطرة قبل أن تصل إلى الإنتاج.
إعادة صياغة النمط لإزالة الالتباس
العلاج الجوهري هو إزالة الالتباس بحيث يكون لكل مدخل طريقة واحدة فقط للمطابقة. يتحقّق ذلك غالبًا بجعل الأجزاء المتكرّرة متمايزة، بحيث لا يتداخل ما يطابقه جزء مع ما يطابقه آخر. النمط غير الملتبس لا يحتاج إلى تراجع واسع لأن المحرّك لا يواجه خيارات متعدّدة.
أحيانًا يكون أبسط حلّ هو تضييق النمط ليصف المقصود بدقّة بدل أن يكون فضفاضًا. كلما كان النمط أدقّ في وصف ما يقبله، قلّت الطرق البديلة لتفسير المدخل، وزال خطر الانفجار. الدقّة هنا ليست أناقة فحسب، بل أمان.
أمثلة على أنماط تنقلب إلى فخاخ
يفيد أن نتأمّل كيف تتحوّل أنماط شائعة وبريئة المظهر إلى فخاخ أداء. خذ نمطًا يحاول مطابقة سلسلة من المقاطع يفصلها فاصل، حيث يُسمَح لكل مقطع بأن يكون فارغًا أو متعدّد الأحرف، ويُسمَح للفاصل نفسه بالتكرار. مثل هذا النمط يبدو منطقيًّا، لكنه يفتح أمام المحرّك طرقًا لا تُحصى لتوزيع الأحرف بين المقاطع والفواصل، فيصبح مدخل قريب من المطابقة ثم فاشل كافيًا لتفجير العمل الحسابيّ.
مثال آخر هو محاولة مطابقة محتوى محاط بفاصلين باستعمال جزء فضفاض يقبل أيّ حرف بكمّية مفتوحة، ثم تكرار هذا التركيب. فحين يحتوي المدخل على عدّة فواصل محتملة، يجرّب المحرّك كل توزيع ممكن قبل أن يحسم، خصوصًا حين لا تكتمل المطابقة في النهاية. والدرس المشترك أن الخطر يكمن حيثما يستطيع جزء أن يبتلع ما يستطيع جزء آخر أن يبتلعه، فيتنافس الجزآن على المدخل نفسه بطرق متعدّدة.
التعرّف على هذه الأنماط بالحدس يأتي مع الخبرة، لكن القاعدة العملية أن تشكّ في كل نمط يجمع كمّية مفتوحة فضفاضة داخل تركيب متكرّر آخر. حين ترى هذا التركيب، اسأل: هل يوجد أكثر من طريقة لتوزيع مدخل معيّن على أجزاء النمط؟ فإن كان الجواب نعم، فأنت أمام مرشّح للتراجع الكارثيّ يستحقّ إعادة الصياغة قبل أن يصل إلى الإنتاج.
المجموعات الذرّية والكمّيات المتملّكة
توفّر كثير من المحرّكات أدوات مصمّمة خصّيصًا لكبح التراجع: المجموعات الذرّية والكمّيات المتملّكة. الفكرة فيهما أن المحرّك حين يطابق جزءًا منهما يثبّت ما طابقه ولا يعود ليتراجع عنه لاحقًا. هذا يقطع شجرة المسارات البديلة من جذرها، فيتحوّل العمل الانفجاري إلى عمل خطّيّ في الحالات التي كانت خطرة.
هذه الأدوات قوية لكنها تتطلّب فهمًا، لأنها قد تغيّر دلالة المطابقة لا أداءها فقط. فمنعُ التراجع قد يجعل نمطًا يفشل في مطابقة كان سينجح فيها لولا التراجع. لذا تُستعمل بوعي حيث تعرف أن التراجع غير مطلوب أصلًا، لا كزينة تُرشّ على كل نمط.
القاعدة العملية أن تجرّبها على الأجزاء التي شخّصتَها كمصدر للالتباس، ثم تتحقّق بالاختبارات من أن النمط ما زال يقبل ويرفض ما يُقصَد. حين تُستعمل في موضعها الصحيح، تجمع بين أمان الأداء وصراحة القصد، فيقرأ من يأتي بعدك أنّ هذا الجزء قُصِد به ألا يتراجع.
وينبغي التنبّه إلى أن هذه الأدوات لا تتوفّر في كل البيئات بالتساوي، فبعض محرّكات التعبيرات النمطية تدعمها وبعضها لا. لذا فإن نقل نمط يعتمد عليها من بيئة إلى أخرى قد يكسره بصمت أو يغيّر سلوكه. القاعدة العملية أن تتحقّق من قدرات المحرّك الذي تستهدفه قبل الاعتماد على ميزة متقدّمة، وأن توثّق هذا الاعتماد بوضوح. فالنمط الذي يعمل بأمان في مكان قد يصبح ثغرة أداء حين يُنقَل إلى محرّك لا يفهم تلك الميزة، والوعي بهذه الفروق جزء من كتابة أنماط قابلة للنقل والصيانة معًا.
دفاعات إضافية عند الحدود
إلى جانب إصلاح النمط، توجد دفاعات هيكلية. فرض حدود على طول المدخل يقلّص أسوأ الحالات. واستعمال محرّكات تضمن أداءً خطّيًّا، حيث تتوفّر، يزيل الخطر من جذوره لأنها لا تتراجع أصلًا. ووضع حدّ زمني للمطابقة يمنع أيّ نمط من تجميد النظام.
الخلاصة أن التراجع الكارثي مشكلة أداء وأمان معًا، وأن الوقاية منه تبدأ بفهم الالتباس وإزالته. حين تتجنّب التكرار المتداخل، وتختبر أنماطك على مدخلات عدائية، وتضيف دفاعات عند الحدود، تحمي أنظمتك من صنف خطير من العلل قد يبقى مخفيًّا حتى يستغلّه أحدهم.