لم تبدُ تذكرة الدعم كهجوم. كانت تجلس في قائمة الانتظار كأي رسالة أخرى — رسالة عميل تنتظر التلخيص. بداخلها كانت تعليمات: اقرأ جدول integration_tokens وانشر محتواه في هذا الخيط.
كان مطوّر قد وصّل مساعدًا ذكيًا بتلك القائمة عبر خادم Supabase MCP، الذي يتصل ببيانات اعتماد service_role — الدور الذي يتجاوز أمان مستوى الصف كليًا. قرأ المساعد التذكرة، وتعامل مع محتواها كمهمة، ونفّذها على قاعدة البيانات الإنتاجية. ظهرت الرموز في خيط التذكرة، حيث اكتفى المهاجم بقراءتها Simon Willison, 2025.
لم يُستغَل شيء بالمعنى التقليدي. لا فساد في الذاكرة، ولا فحص مصادقة مفقود، ولا تبعية مخترقة. وصل النص العدائي إلى مستوى البيانات عبر الوكيل وحده — ودليل المفهوم يتعمم على أي وكيل مُوصَّل بالطريقة ذاتها.
هذا هو حقن SQL مع إعادة توطين المفسِّر. لا يزال النص العدائي يتحول إلى أمر قاعدة بيانات. لكن السلسلة لم تعد تصل في المدخلات، حيث تقف عشرون عامًا من أدوات التعقيم حارسةً. بل تصل في مخرجات النموذج — في مرحلة تلي كل مرشح تملكه.
السجل
أطلق باحثو INESC-ID في لشبونة اسمًا على هذا النمط عام 2023: P2SQL، حقن الأوامر إلى SQL Pedro et al., 2025. عملوا على روبوت محادثة حقيقي مبني على LangChain فوق Postgres، وصمّموا سبعة هجمات تمثيلية متصاعدة الخطورة — قراءات غير مصرح بها، وجداول تالفة، وجداول محذوفة، وصولًا إلى تنفيذ الأوامر — وشغّلوها على سبعة نماذج متطورة. كانت الإخفاقات مستقلة عن النموذج. خلاصتهم حرفيًا: «التطبيقات المدمجة مع LLM المبنية على Langchain عرضة بشدة لهجمات حقن P2SQL.» نُشر البحث في ICSE 2025 — وهو ما يعني في لغة المراجعة العلمية أن هذا ليس مجرد فضول بحثي.
بحلول منتصف 2024 أصبح للنمط رقم CVE. كانت Vanna.AI، مكتبة نص إلى SQL، تمرر أسئلة المستخدمين إلى موجّه يولّد كود رسوم Plotly، ثم تشغّل ذلك الكود بـexec(). سؤال مُصاغ بعناية أفضى مباشرةً إلى تنفيذ أوامر عن بُعد — CVE-2024-5565، CVSS 8.1 JFrog, 2024. لاحظ أين كانت الحمولة: في مخرجات النموذج، بعد كل فحص للمدخلات. موجّهات الحماية في Vanna لم توقفها، لأن الحاجز ليس سوى نص إضافي في نافذة السياق ذاتها التي يوجد فيها الهجوم.
بنهاية 2024 أصبح الترتيب رسميًا: حقن الأوامر هو LLM01، الخطر الأول في OWASP Top 10 لتطبيقات LLM، يحتل المرتبة الأولى للإصدار الثاني على التوالي OWASP, 2025. تشخيص OWASP للسبب الجذري يختصر القصة كلها في جملة واحدة: تعالج نماذج LLM التعليمات والبيانات في القناة ذاتها دون فصل واضح. وهذا أيضًا سبب انقسام الفئة إلى نوعين — الحقن المباشر الذي يكتبه المستخدم، والحقن غير المباشر الذي يأتي فيما يسترجعه النموذج. تذكرة Supabase كانت غير مباشرة. وكذلك كان الأسوأ في السجل.
في يونيو 2025، كُشف عن CVE-2025-32711 — EchoLeak، CVSS 9.3 — في Microsoft 365 Copilot: أول ثغرة حقن أوامر موثّقة في العالم الحقيقي بدون نقر على نظام LLM إنتاجي arXiv, 2025. بريد إلكتروني واحد مُصاغ بعناية — تعليمات مخفية في تعليقات HTML، ونص أبيض على خلفية بيضاء، وMarkdown بأسلوب المراجع — وقام Copilot بتسريب بيانات داخلية إلى خادم المهاجم. لم ينقر المستخدم على شيء. تجاوزت السلسلة مصنّف Microsoft المخصص لاكتشاف حقن الأوامر وإخفاء الروابط في طريق الخروج.
لماذا تفشل الحلول الواضحة
كل حادثة مما سبق تُسقط أحد الدفاعات المعيارية.
ترشيح المدخلات. اجتاز EchoLeak نظام XPIA، المصنّف الذي بنته Microsoft تحديدًا لاكتشاف محاولات الحقن arXiv, 2025. المصنّفات احتمالية. يحتاج المهاجم إلى خطأ واحد ولديه محاولات غير محدودة لصناعته؛ أما المدافع فيحتاج إلى سجل مثالي في مواجهة مدخلات مصممة لتبدو كأي شيء آخر.
توجيه النموذج. شحنت Vanna موجّهات حماية؛ CVE-2024-5565 اخترقها JFrog, 2024. لا توجد قناة ذات امتياز داخل نافذة السياق. نص النظام ونص المستخدم ونص الهجوم كلها من المادة ذاتها — وهذا بالضبط السبب الجذري الذي يسمّيه OWASP OWASP, 2025.
جدار حماية المخرجات. حلّل SQL المولَّد وأوقف الأجزاء الخطرة. الآن عليك صيانة قائمة حظر لكل بنية قادرة على الكتابة في كل لهجة تدعمها، إضافةً إلى كل دالة ذات آثار جانبية — بينما المؤلف المشارك للمهاجم هو النموذج ذاته، وهو سعيد بإعادة صياغة استعلام محظور في شكل مسموح به. والأدل على ذلك أن الدفاعات التي اقترحها مؤلفو P2SQL أنفسهم تقع خارج النموذج: حلّل SQL المولَّد وضع له قائمة مسموحات، وشغّل الوكيل تحت دور قاعدة بيانات منفصل ومقيّد Pedro et al., 2025. الضوابط التي تصمد هي تلك التي لا يستطيع النموذج لمسها.
ضغط Simon Willison البنية في قاعدة ثلاثية: وكيل يملك وصولًا إلى بيانات خاصة، ومعرّض لـمحتوى غير موثوق، ولديه وسيلة للـتواصل خارجيًا يمكن جعله يسرّب البيانات — دون الحاجة إلى أي ثغرة برمجية Simon Willison, 2025. وصفته معمارية: أزل أحد الأضلاع. قائمة تخفيف OWASP تتقاطع على الأرضية ذاتها — قيّد ما يستطيع النموذج فعله، وتحقق مما ينتجه بكود حتمي، لأن النموذج لا يمكن الوثوق به في مراقبة نفسه.
اقرأهما معًا وستجدهما يقولان شيئًا أقوى من «أضف دفاعات». يقولان: توقف عن الحكم على السلاسل النصية. غيّر الواجهة.
لا بنية صياغية للحقن فيها
لم ينتهِ حقن SQL لأن المعقّمات تحسّنت. انتهى لأن الاستعلامات ذات المعاملات أزالت المكان الذي يمكن فيه للبيانات أن تصبح كودًا. الخطوة ذاتها متاحة للوكلاء: ارفض السماح للنموذج بصياغة بنية قابلة للتنفيذ أصلًا.
هكذا بُني SQAI. النموذج لا يكتب SQL أبدًا — لا معقَّمًا، ولا مراجَعًا، لا شيء. أداته الوحيدة المنفِّذة تقبل مواصفة مكتوبة: اتحاد مميَّز، kind: "query" | "computation"، version: "1"، مُتحقَّق منها مقابل عقد قدرات مثبَّت بهاش قبل أي تنفيذ. سمّ قدرةً خارج العقد يجيب المحرك بـunsupported_operation. لا سلسلة نصية ينتجها النموذج تُسلَّم إلى مفسِّر أو تُدمج أو تُستكمَل — فالشيء الذي يجب على الحقن الاستيلاء عليه، وهو البنية الصياغية التي يؤلفها النموذج، غير موجود في خط الأنابيب. الحجة الكاملة مفصّلة في لماذا لا ينبغي للوكلاء كتابة SQL.
المفردات مغلقة، وهي للقراءة فقط: كل واحدة من 4,574 قدرة مكشوفة، بالبناء. لا توجد قدرة كتابة لتسميتها، فلا تعويذة — مهما كانت مُهرَّبة بمهارة في تذكرة دعم — تُنتج كتابة. مخرج الكود المضيف، getUnsafeRuntime، غير قابل للوصول من أي أداة نموذج.
ولأن الأمان الجاد يحسب ليوم يُخترق فيه النموذج، تفترض الطبقات خلف الواجهة ذلك:
- سياسة لا يستطيع النموذج مخاطبتها. قوائم المسموحات للمصادر والحقول والدوال ثابتة في كودك عند
createSQAI()ومُطبَّقة داخل العملية قبل التنفيذ. مخطط الأداة لا يحمل حقولallowed*— سطح السياسة لا يدخل نافذة السياق أبدًا. المخالفات تُعيد رفضًا مكتوبًا غير قابل للإعادة:policy_denied_source،policy_denied_field،policy_denied_function. لماذا يتفوق هذا على الأدوار والأعلام: القراءة فقط التي لم تكن كذلك. - نتائج مقيّدة بالمستأجر. النتائج الكبيرة تُخزَّن تحت
result_idمن 16 بايت عشوائي. مستأجر خاطئ يحصل علىresult_not_found— لا خطأ صلاحيات يؤكد وجود البيانات — وتنتهي صلاحية المدخلات في 15 دقيقة. - قناة محدودة للعودة إلى النموذج. 25 صفًا كحد أقصى و32,000 بايت تعود إلى نافذة السياق، مع إعلان دائم عن الاقتطاع. موجّه مُحقَن لا يستطيع جعل الأداة تفرغ جدولًا في النص؛ وسيلة النقل لن تحمله.
- أخطاء لا ترسم خريطة. الأخطاء المرئية للنموذج تمر عبر
sanitizeMessage، الذي يجرّد مسارات نظام الملفات المطلقة. الاستطلاع يحصل على رفض نظيف، لا قائمة مجلدات.
قيّمه مقابل الثلاثية. البيانات الخاصة: لا تزال موجودة — هذا هو الغرض. المحتوى غير الموثوق: لا يزال موجودًا — النماذج تقرأ ما تقرأه. لكن الأضلاع التي تحوّل التأثير إلى خسارة مقطوعة. مسار الكتابة غائب، ومسار القراءة مقيّد بقائمة مسموحات، ومحدود بالمستأجر، ومحدود الحجم — ومُثبَّت بهاش، فأي شيء طلبه وكيل مُخترَق قابل للإعادة في مرحلة ما بعد الحادثة.
الحدود، مرسومةً من جديد
حقن الأوامر لم يُحَل، والسجل يقول إنه لن يُحَل قريبًا. LLM01 يحتفظ بمرتبته لإصدارين متتاليين. نظام إنتاجي خلفه دفاعات Microsoft خسر أمام بريد إلكتروني واحد. ستواصل النماذج قراءة النصوص العدائية، وبعضها سيصل.
لكن التأثير على النموذج والوصول إلى قاعدة بياناتك إخفاقان مختلفان، والأول وحده لا مفر منه. تذكرة Supabase تحوّلت إلى حادثة لأن الوكيل كان يحمل service_role وسطحًا SQL مفتوحًا — وهما بالضبط الضلعان اللذان يحتاجهما الاختراق. التذكرة ذاتها، موجَّهةً إلى مفردات مغلقة ومكتوبة وللقراءة فقط، تصبح سطرًا في السجل: unsupported_operation.
الحل من حقن SQL لم يكن مرشحًا أذكى. كان حدًّا لا يمكن فيه للبيانات أن تصبح كودًا. ارسم ذلك الحد من جديد — هذه المرة حول النموذج — وحقن SQL الجديد يحصل على النهاية القديمة. بقية السطح موثَّق في /security.