يوضح هذا الدليل بالتفصيل الطريقة التي تعمل فعليًا: التقييد الهيكلي لـ SELECT، والقائمة البيضاء المغلقة للوظائف، وغطاء الصف الإلزامي، وقفل المخطط الذي تم الاستعلام عنه. تعتمد كل خطوة على أداة التحقق التي تم تنفيذها بالفعل في محرك NL2SQL الخاص بـ Aurabase، وهي قدرة AI الأصلية المضمنة في الواجهة الخلفية، وليست خدمة تابعة لجهة خارجية يتم تجميعها معًا بعد حدوثها. إذا كان الموضوع جديدًا بالنسبة لك، فإن نظرة عامة على NL2SQL تضع الأسس، ويوضح البرنامج التعليمي خطوة بخطوة كيفية إنشاء نقطة النهاية الكاملة.
الأساسيات
- الهندسة السريعة ("يولد SELECT فقط") هي ليست فحصًا أمنيًا: يمكن للنموذج أن يهذي، أو يسترشد بسؤال غامض، أو ببساطة يتجاهل التعليمات.
- التحقق من الصحة يكون بنيويًا: يقوم المحلل اللغوي ببناء الشجرة النحوية (AST) للطلب ويرفض افتراضيًا أي شيء غير مصرح به بشكل صريح.
- أربع طبقات ملموسة تحد من المخاطر: تحديد صارم (لا استعلام فرعي، ولا CTE، ولا UNION)، والقائمة البيضاء المغلقة المكونة من عشر وظائف،
LIMITإلزامي ومحدود، الوصول المحظور إلى كتالوج النظام والمخططات غير المستأجرة. - يجب أن يأتي المخطط الذي تم الاستعلام عنه من خادم ، وليس من حقل في طلب العميل أبدًا: وإلا فلا شيء يمنع المتصل من توفير المخطط الخاص به لتجاوز التحقق من الصحة.
- في Aurabase، يتم اختبار أداة التحقق هذه (Rust crate
sqlparser) مع حالات عدائية موثقة في التعليمات البرمجية: الوظائف المحظورة المخفية فيFILTERأو في التجميع الداخليORDER BYأو فيOFFSET.
لماذا لا تحظر التعليمات الواردة في النظام أي شيء؟
يعد موجه النظام الذي يقول "ينشئ استعلامات SELECT فقط" تفضيلًا وليس عائقًا. يحترمه النموذج في أغلب الأحيان لأنه تم تدريبه على اتباع التعليمات، وليس لأن قيدًا فنيًا يمنعه فعليًا من كتابة أي شيء آخر. وهناك فئتان من الفشل تجعلان هذه الثقة غير كافية في الإنتاج.
الأول يأتي من السؤال نفسه. يمكن للمستخدم، ذو النية السيئة أو ببساطة المبدع في صياغته، توجيه السؤال بطريقة تدفع النموذج نحو SQL الذي لم يكن من المفترض أن يكتبه: ربط بجدول حساس، مرشح يتحايل على المنطق المتوقع، استدعاء وظيفة النظام. لا يميز النموذج بين السؤال المشروع والسؤال المصمم للتلاعب به.
والثاني لا يحتاج إلى خبث. يمكن للنموذج أن يهلوس اسم جدول، أو ينسى LIMIT الذي طلبه الموجه، أو ينشئ SELECT * دون أي قيود على جدول كبير. والنتيجة هي نفسها في كلتا الحالتين: لغة SQL قد تكون باهظة الثمن أو متطفلة، والتي اجتازت مرشح المطالبة وهي على وشك التنفيذ على قاعدة بيانات حقيقية.
يظل موجه النظام مفيدًا، فهو يوجه النموذج نحو النتيجة الصحيحة في معظم الأوقات. لكن علامة "ممنوع الوصول" لا تمنع أي شخص لا يعرف القراءة، أو من يقرر تجاهلها. أنت بحاجة إلى باب مغلق في الخلف، وليس مجرد لوحة في الأمام.
قم بتحليل SQL الذي تم إنشاؤه في شجرة بناء الجملة، وليس في سلسلة أولية أبدًا
خط الدفاع الأول هو تحليل SQL التي ينتجها النموذج باستخدام محلل حقيقي للهجة المستهدفة، ثم التحقق من صحة البنية الناتجة، وليس النص الخام. يتم تجاوز البحث عن الكلمات المحظورة في سلسلة الأحرف ("DROP"، "DELETE"، "؛") بشكل تافه: حالة مختلفة، إدراج تعليق في منتصف الكلمة الرئيسية، علامات الاقتباس المكتوبة. تصف شجرة بناء الجملة بشكل لا لبس فيه ما يفعله الاستعلام بالفعل.
تنفذ Aurabase هذه الخطوة باستخدام صندوق Rust sqlparser ولهجته PostgreSqlDialect. حتى قبل التحليل، يرفض المرشح المعجمي الأول تركيبين يصعب تفسيرهما بشكل صحيح مرة واحدة في الشجرة: الاقتباس بالدولار ($$...$$)، والذي يمكنه إخفاء محتوى عشوائي في سلسلة، والتعليقات متعددة الأسطر (/* */)، والتي يمكن أن تخفي النهاية الحقيقية للتعليمة.
يؤدي هذا الرفض للبيانات المتعددة وحدها إلى حظر الشكل الأكثر شهرة لحقن SQL عن طريق التراص: SELECT * FROM users; DROP TABLE users;--. يقوم المحلل اللغوي بإرجاع تعليمة واحدة قابلة للتنفيذ فقط، أما الثانية فلا يتم الوصول إليها أبدًا، بغض النظر عن كيفية صياغتها في السؤال الأصلي.
تقييد هيكليًا على SELECT بسيط
بمجرد الحصول على الشجرة، يكون أوسع نطاق للتحقق هو قبول نوع واحد فقط من عقدة الجذر، وهو الاستعلام (Statement::Query)، ورفض كل شيء آخر: INSERT, UPDATE, DELETE, DROP, CREATE, ALTER. لم تعد تعليمات سريعة، بل هي شرط لنوع الكائن الذي تم تحليله، والذي لا يمكن لأي صياغة ماهرة للسؤال التحايل عليه.
حتى داخل SELECT، تظل العديد من البنيات خطيرة وتستحق رفضها الصريح:
| البناء مرفوض | لماذا هو خطير؟ |
|---|---|
| CTE / مع | يمكن ربط المنطق الإضافي غير المقصود قبل التحديد النهائي. |
| الاستعلامات الفرعية، الاتحاد / التقاطع / باستثناء | يوسع المساحة السطحية لما يمكن أن يفعله سؤال واحد في استعلام واحد. |
| اختر...في | ينشئ جدولاً: الكتابة متنكرة في زي القراءة. |
| للتحديث / للمشاركة | تركيب الأقفال، خطر التعارض مع حركة الإنتاج. |
| وظائف الجدول (generate_series، pg_read_file...) | الوصول إلى النظام أو رفض الخدمة عبر الخطوط التي يتم إنشاؤها حسب الطلب. |
توضح حالة الاختبار المأخوذة من المستودع بشكل ملموس النقطة الأخيرة: تم رفض SELECT * INTO backup FROM users، على الرغم من أنه لا يحتوي على كلمة رئيسية مكتوبة مرئية ولا وظيفة مشبوهة. شكل الطلب يكفي لاستبعاده.
وظيفة القائمة البيضاء، وليس القائمة السوداء
تتطلب القائمة السوداء للوظائف المحظورة (pg_sleep, pg_read_file, dblink...) توقع كل وظيفة خطيرة واحدة تلو الأخرى، بينما يكشف Postgres عدة مئات منها. تعكس القائمة البيضاء عبء الإثبات: عشر وظائف مسموح بها فقط، count, sum, avg, min, max, lower, upper, coalesce, date_trunc, now. كل شيء آخر غير مسموح به افتراضيًا، بما في ذلك ميزة شرعية واحدة لم يفكر أحد في إضافتها بعد.
تمريرة التحقق الواحدة ليست كافية دائمًا. يسرد الاجتياز الهيكلي للشجرة نقاط الدخول الخاصة بها واحدة تلو الأخرى (إسقاط، أين، JOIN، GROUP BY...)، ومن السهل نسيان واحدة: يمكن إخفاء وظيفة محظورة في جملة FILTER (WHERE pg_sleep(10) IS NOT NULL)، في التجميع الداخلي ORDER BY (sum(id ORDER BY pg_sleep(10)))، في WITHIN GROUP، DISTINCT ON، أو OFFSET.
وبالتالي، يضيف مدقق Aurabase ممرًا شاملاً ثانيًا، والذي يمر عبر جميع التعبيرات الموجودة في الشجرة أينما كانت، بشكل مستقل عن المسار الهيكلي. إنه دفاع مفترض في العمق: إذا أخطأت التمريرة الأولى إحدى الهجمات، فإن الثانية تلحق بها.
ربط الخطوط المرتجعة: LIMIT إلزامي ومحدد
يظل SELECT * معتمدًا، وهو مفيد لاستخراج البيانات. الخطر ليس هو النجم، بل هو عدم وجود سقف للاستعلام المكتوب بواسطة نموذج: يمكن أن يؤدي السؤال الذي تمت صياغته بشكل سيئ إلى إعادة جدول بأكمله، مع تكلفة الذاكرة ووقت الاستجابة الذي يتضمنه ذلك.
تطبق Aurabase قاعدة بسيطة وشفافة. إذا لم يكن لدى SQL الذي تم إنشاؤه LIMIT، فسيضيف الخادم واحدًا (100 سطر بشكل افتراضي، يتم الإعلان عن القيمة للنموذج في موجه النظام). إذا طلبت SQL LIMIT خارج الحد الأقصى (1000 صف بشكل افتراضي)، فسيتم رفض الاستعلام بشكل صريح بدلاً من تقليله بصمت. كلا القيمتين قابلتان للتكوين من جانب الخادم (AI_NL2SQL_DEFAULT_LIMIT, AI_NL2SQL_MAX_LIMIT)، ويرفض الخادم البدء إذا تجاوز الخطأ الحد الأقصى.
إن الرفض بدلاً من التراجع بصمت له مصلحة مباشرة: فالسقف المطبق دون أن يقول ذلك من شأنه أن يوهم المتصل بأن طلبه قد تم الوفاء به، في حين أن النتيجة سوف يتم اقتطاعها دون أن يعلم بذلك. limit_injected يشير دائمًا إلى ما إذا كانت القيمة تأتي من النموذج أو الخادم.
تأمين الوصول إلى المخطط: كتالوج النظام والمخطط المشترك
هناك تسريبان متميزان يهددان محرك NL2SQL متصل بقاعدة بيانات حقيقية: الوصول إلى كتالوج نظام Postgres، والوصول إلى مخطط لا ينتمي إلى المتصل. يتم حظر كلاهما عند التحقق من الصحة، بغض النظر عن أي سياسة RLS يتم وضعها في اتجاه مجرى النهر.
pg_catalog دائمًا جزء من search_path، مما يعني أن الاسم غير المؤهل مثل pg_authid أو pg_stat_activity يصل إليه مباشرة، بدون بادئة. يحظر مدقق Aurabase أي اسم يبدأ بـ pg_، بالإضافة إلى information_schema والمخطط الداخلي aura_console، سواء كان مؤهلاً أم لا.
في الاسم المكون من مكونين (schema.table)، يُسمح فقط بمخطط مشروع الاستدعاء، ويتم رفض أي قيمة أخرى. سيتم رفض الاسم الذي يحتوي على ثلاثة مكونات أو أكثر تلقائيًا. هذا الحد على مستوى الاستعلام الذي تم إنشاؤه بالإضافة إلى عزل مستوى قاعدة البيانات المفصل في مقالتنا حولعزل المستأجرين المتعددين: أحدهما يمنع SQL الذي تم إنشاؤه من استهداف مخطط آخر، والآخر يمنع الاتصال نفسه من الوصول إلى قاعدة بيانات أخرى. ولا يحل أي منهما محل الآخر.
لا تدع العميل يعيد تعريف المخطط الذي تم الاستعلام عنه أبدًا
تكمن المصيدة المنفصلة في انتظار أي واجهة برمجة تطبيقات NL2SQL تقبل معلمة تصف المخطط أو الجداول المسموح بها في استعلام العميل. إذا تم استخدام هذه المعلمة نفسها لإنشاء الموجه والتحقق من صحة إخراج SQL، فيمكن للمتصل أن يكذب بشأن ما هو مسموح به، ثم يتم التحقق من الصحة مقابل هذه الكذبة بدلاً من مقارنتها بواقع قاعدة البيانات.
يتأمل Aurabase المخطط الأساسي للمشروع الفعلي في كل مكالمة، مع ذاكرة تخزين مؤقت قصيرة مدتها ثلاثون ثانية للأداء، ويرفض صراحةً (خطأ 400) أي حقول schemaأو allowed_schemaأو schema_context مرسلة في نص الطلب، بدلاً من قبولها ثم الكتابة فوقها بصمت. الفرق مهم: المجال الذي يتم قبوله ثم تجاهله يعطي وهم السيطرة غير الموجودة؛ الحقل المرفوض يقول ذلك على الفور.
قم بمراجعة مسار NL2SQL الخاص بك قبل الإنتاج
سواء كنت تستخدم Aurabase أو تقوم ببناء مسارك الخاص فوق دراسة LLM عامة، فإن النقاط التالية تغطي ما يتم تفويته غالبًا.
إذا قمت بكتابة المدقق بنفسك
- قم بتحليل لغة SQL باستخدام محلل حقيقي لهجتك المحددة، ولا تستخدم مطلقًا مطابقة النمط في السلسلة.
- اعتماد الرفض الافتراضي: يجب رفض أي نوع من العقد أو أي وظيفة غير مصرح بها بشكل صريح، وليس فقط الحالات الخطيرة التي تم تحديدها بالفعل.
- اقبل عبارة واحدة فقط لكل استعلام، وهذا هو أبسط رفض مقابل تكديس الاستعلامات.
- اختبر أداة التحقق مع حالات خصومة حقيقية (الوظيفة محظورة في FILTER، في التجميع الداخلي ORDER BY، في OFFSET)، وليس فقط مع الحالات الواضحة.
- على الرغم من كل شيء، قم بتنفيذ SQL الذي تم التحقق من صحته باستخدام دور Postgres مع امتيازات مخفضة في المخطط المتوقع: يحد المدقق من شكل الاستعلام، ويحد الدور مما يمكنه تحقيقه فعليًا إذا أفلتت منك حالة ما.
إذا كنت تقوم بتقييم إطار عمل NL2SQL لجهة خارجية
- اسأل بوضوح ما إذا كان التحقق بنيويًا (AST) أم مجرد تعليمات سريعة: فالإجابة تغير كل شيء.
- تأكد من تطبيق الحد الأقصى للسطر بشكل افتراضي، وليس فقط توثيقه كأفضل ممارسة على نفقتك الخاصة.
- تحقق مما إذا كان من الممكن توفير المخطط المستخدم للتحقق من الصحة بواسطة عميل واجهة برمجة التطبيقات، مما سيؤدي إلى إعادة فتح الخلل الموضح أعلاه تمامًا.
- قارن بين العديد من الأدوات وفقًا لهذا المعيار المحدد قبل الاختيار: توضح مقارنة الخاصة بنا لأدوات NL2SQL تفاصيل ما يميز الأساليب المتاحة في عام 2026.
يقلل المدقق من المخاطر، ولا يحل محل RLS
يعمل مدقق AST القوي على تقليل المخاطر عند المصدر: يحتوي SQL الذي يصل إلى قاعدة البيانات الخاصة بك بالفعل على نموذج معروف ومحدود. ومع ذلك، فهو لا يحل محل سياسات RLS في جداولك الحساسة، والتي تحدد الصفوف التي يحق لمستخدم معين رؤيتها. تجيب الطبقتان على أسئلة مختلفة: يقوم المدقق بتحديد شكل الاستعلام الذي تم إنشاؤه، ويحدد RLS البيانات التي يمكنه إرجاعها لمستخدم معين. أبقِ كلاهما نشطًا، حتى لو بدا أحدهما زائدًا عن الآخر.
يغطي NL2SQL الأسئلة المنظمة حول جداولك. بالنسبة للأسئلة حول المحتوى غير المنظم والمستندات والملاحظات والتذاكر، يتبع RAG الأصلي لـ Aurabase منطق أمان مشابه، تم تفصيله في البرنامج التعليمي لخط أنابيب RAG على pgvector.