Security
صلاحيات نوافذ الصيانة في PGEE: تغيير المخطط فقط عند الموافقة
تذاكر لجنة التغيير لا تلغي GRANT. كيف تمنع صلاحيات الصيانة المرتبطة بالوقت في PGEE أمر ALTER TABLE في منتصف اليوم، ولماذا يبقى حل محفّزات الأحداث ناقصًا.

وافقت لجنة التغيير على التعديل ليوم الأحد الساعة 02:00. التذكرة خضراء. في الساعة 14:17 من يوم تداول يشغّل أحدهم، بدور DBA دائم، أمر ALTER TABLE … ADD COLUMN على بيئة الإنتاج، لأن الإصدار معلّق و«التغيير صغير». تتراكم انتظار الأقفال. أو أسوأ: يمرّ التغيير بهدوء، ولا يُلاحظ إلا حين يطلب عيّنة التدقيق النافذة التي لم تُفتح أصلًا.
نجحت العملية. قاعدة البيانات لم تفرضها. PostgreSQL المجتمعي يقبل بسرور صلاحية GRANT تدوم إلى الأبد، وأمر ALTER لا يعرف مفهوم «نافذة صيانة». أما CYBERTEC PostgreSQL Enterprise Edition (PGEE) فيدرج ميزة لهذا الفراغ تحديدًا: صلاحيات مرتبطة بالوقت / صلاحيات صيانة قائمة على قواعد. تربط الصلاحيات بنوافذ صيانة، فتُمنع تغييرات بنية البيانات خارج النافذة وتُسمح داخلها. فيما يلي: مشكلة الصلاحية الدائمة، والحل المجتمعي الذي تجربه الفرق أولًا (ولماذا يبقى ناقصًا)، وما تقوله وثائق PGEE العامة فعلًا، وكيف تُقرن الميزة بسجل التدقيق وبفروق الصلاحيات التصريحية.
الصلاحية الدائمة هي الخطر الحقيقي
في بيئة خاضعة للتنظيم، لجنة التغيير موجودة أصلًا. الفجوة في الإنفاذ على مستوى الكائنات:
صلاحية
GRANT CREATE, ALTER(أو دور يملكها فعليًا عبر العضوية) دائمة إلى أن يتذكّر أحدهمREVOKE. التذاكر تُغلق. الصلاحيات لا تُغلق.ضغط إصدار التطبيق يحوّل «انتظر النافذة» إلى «هذه المرة فقط». من يستطيع الإطلاق غالبًا هو من يستطيع تنفيذ DDL.
عيّنات التدقيق تبحث عن دليل أن تغييرات المخطط حدثت داخل نافذة معتمدة. سجل في ServiceNow يقول الأحد، وكتالوج قاعدة البيانات يقول الخميس 14:17، هذه ملاحظة، حتى إن لم يقصد أحد الأذى.
أقل صلاحية لعمليات DML نقاش محسوم في أغلب البنوك. أقل صلاحية لـ DDL، *وأقل صلاحية تتغيّر مع الساعة*، ليست كذلك. هذا هو جمهور الميزة: قادة DBA ومهندسو المنصات/الأمن الذين لديهم لجنة تغيير وما زالوا يرون تغييرات مخطط في منتصف اليوم.
ما يتيحه PostgreSQL المجتمعي (وما يفوته)
محفّزات الأحداث (event triggers) هي الحل اليدوي المعتاد. بل إن التوثيق الرسمي يورد مثالًا على table_rewrite يرمّز نافذة صيانة عبر current_time:
CREATE OR REPLACE FUNCTION no_rewrite() RETURNS event_trigger LANGUAGE plpgsql AS$$DECLARE table_oid oid := pg_event_trigger_table_rewrite_oid(); current_hour integer := extract('hour' from current_time); pages integer; max_pages integer := 100;BEGIN IF pg_event_trigger_table_rewrite_oid() = 'public.foo'::regclass THEN RAISE EXCEPTION 'you''re not allowed to rewrite the table %', table_oid::regclass; END IF; SELECT INTO pages relpages FROM pg_class WHERE oid = table_oid; IF pages > max_pages THEN RAISE EXCEPTION 'rewrites only allowed for table with less than % pages', max_pages; END IF; IF current_hour NOT BETWEEN 1 AND 6 THEN RAISE EXCEPTION 'rewrites only allowed between 1am and 6am'; END IF;END;$$;CREATE EVENT TRIGGER no_rewrite_allowed ON table_rewrite EXECUTE FUNCTION no_rewrite();
المثال حقيقي ومفيد للتعلّم. وهو أيضًا ناقص كآلية ضبط تغييرات في الإنتاج. اقرأ التوثيق نفسه بعناية:
table_rewriteضيّق النطاق. يطلق مباشرة قبل إعادة كتابة جدول ببعض إجراءاتALTER TABLEوALTER TYPE. وينص التوثيق صراحة على أنCLUSTERوVACUUMلا يطلقانtable_rewrite، رغم أنهما قد يعيدان كتابة الجدول.ddl_command_startأوسع، وما زال غير شامل. يطلق مباشرة قبلCREATEوALTERوDROPوCOMMENTوGRANTوIMPORT FOREIGN SCHEMAوREINDEXوREFRESH MATERIALIZED VIEWوREVOKEوSECURITY LABEL(وSELECT INTO). لا يطلق على DDL للكائنات المشتركة: قواعد البيانات، والأدوار (تعريفها وعضويتها)، ومساحات الجداول، وصلاحيات المعاملات، وALTER SYSTEM. ولا يطلق على الأوامر التي تستهدف محفّزات الأحداث نفسها.حساب superuser يستطيع إزالة الحارس. الأمر
DROP EVENT TRIGGER no_rewrite_allowed;ينهي السياسة. إذا كان دور كسر الزجاج الطارئ هو نفسه تسجيل دخول الـ DBA اليومي، فالنافذة اختيارية.current_timeيتبعTimeZoneللجلسة. جلستان بإعدادَيTimeZoneمختلفين تريان «ساعات» مختلفة. مزامنة الساعات عبر خوادم التطبيقات وJump hosts ومضيف قاعدة البيانات نظافة تشغيل عادية. أما اعتبار وقت الجلسة حدًّا أمنيًا فليس كذلك.ملكية دالة المحفّز مهمة. دالة
SECURITY DEFINERتعمل بمالك قوي فخ كلاسيكي إن أخطأ مسار البحث أو مسار الاستثناء. ودالةSECURITY INVOKERيستطيع دور الـ DBA استبدالها فخ من نوع آخر.
يمكنك الجمع بين ddl_command_start ومرشّح WHEN TAG IN (...) وجدول سماح مركزي. كثير من الفرق يفعل ذلك. وما زلت لا تحصل من PostgreSQL المجتمعي على مفهوم أصيل بأن *الصلاحيات نفسها* لا تسري إلا داخل نافذة. تحصل على بوابة إجرائية تصونها أنت، يستطيع دور مميز تفكيكها، وتفوت DDL الكائنات المشتركة تصميمًا.
ما تقوله وثائق PGEE العامة فعلًا
تدرج مقارنة ميزات CYBERTEC "Time related permissions" هكذا: PostgreSQL المجتمعي: غير متاحة أصلًا. PGEE: مضمّنة.
وفي ملف Enterprise PostgreSQL Security (v1.1، 2025-04-11)، ضمن قسم "RULE BASED MAINTENANCE PERMISSIONS"، تُذكر نية المنتج نثرًا:
> PGEE allows you to associate permissions with maintenance windows. You can prevent changes to your data structure during certain periods while allowing them during a maintenance window.
هذه هي الدعوى التي نعتمدها. اقرأ الحدود بعناية تساوي وعد الميزة:
ميزة منتج. تربط الصلاحيات بالنوافذ، موجّهة لضبط تغيير بنية البيانات. استدلالنا العملي، ونسمّيه استدلالًا، أن «بنية البيانات» هنا تعني تغييرات من فئة المخطط/DDL (جداول وفهارس وما شابه)، وهو نمط الفشل في قصة لجنة التغيير أعلاه. أكّد نطاق الكائنات الدقيق لإصدار PGEE لديك أثناء التقييم.
لا واجهة SQL عامة منشورة في المواد التي نستشهد بها. يتعمّد هذا المقال ألا يخترع صيغة
GRANT … DURING WINDOW، ولا ربطًا تلقائيًا مع ServiceNow، ولا اتفاقية مستوى خدمة حول تبديل النوافذ. إن كان بناء التقييم لديك يعرض واجهة محددة لتعريف النافذة فاستخدمها. لا تفترض واحدة من مقال مدوّنة.ليست شهادة امتثال. ربط الصلاحيات بالنوافذ يساعدك على *تنفيذ* ممارسات ضبط التغيير وأقل صلاحية التي يسأل عنها المدققون. وهو بذاته لا «يفي» بأي إطار مسمّى.
المواد الأمنية نفسها تصوّر قدرة مرافقة تحت إدارة الصلاحيات المتكاملة / التصريحية: رؤية كل الصلاحيات، وتعريف حالة مرغوبة، وحساب المرغوب مقابل الواقع، والإصلاح تلقائيًا. في العالم مفتوح المصدر هذه المرافقة هي إضافة CYBERTEC pg_permissions (permission_target وpermission_diffs() وعروض يمكن تحديث عمود granted فيها لتشغيل GRANT/REVOKE). يقع تأطير منتج PGEE لـ «التدقيق الأمني المتكامل» بجوار هذه الفكرة. كن دقيقًا بشأن SQL الذي تشغّله. واجهة الإضافة مفتوحة المصدر عامة. تعامل مع تغليف PGEE للقصة الأوسع كنطاق منتج يُؤكَّد في التقييم، لا كلغة ثانية مخترعة.
لماذا توجد ميزة المنتج إن كان الحل اليدوي «يعمل»
لأن نمط الفشل اجتماعي ودائم. النحو ليس الجزء الصعب.
محفّز حدث يدوي يجيب: «امنع هذا الأمر الآن إن كانت الساعة خاطئة.» أما GRANT دائم فيجيب: «هذا الدور يجوز له تغيير المخطط أي يوم في الأسبوع.» سؤالان مختلفان. عملية لجنة التغيير تحاول الإجابة عن الثاني بالتذاكر. قاعدة البيانات تجيب عن الثاني بالصلاحيات. ميزة PGEE المعلنة تجعل الصلاحية نفسها مقيّدة زمنيًا لتغييرات بنية البيانات. فيفشل ALTER TABLE في منتصف اليوم لأن الصلاحية ليست سارية. ذلك ضابط مختلف عن دالة محفّز كتبتها وتذكّرت فحص extract(hour …).
هذا مهم حين:
الشخص تحت ضغط الإصدار هو superuser، يستطيع إسقاط محفّزك اليدوي.
التغيير ليس إعادة كتابة جدول (فلا يطلق
table_rewriteأصلًا).التغيير هو
GRANT/REVOKEعلى كائن، أوDROP INDEX، أو شيء نسيه قائمةWHEN TAG.محفّز الربع الماضي عُطّل «مؤقتًا» أثناء ترحيل ولم يُعد تفعيله.
هذا ليس طعنًا في محفّزات الأحداث. استخدمها. اعرف ثغراتها. ولا تخلط بينها وبين الصلاحيات المرتبطة بالوقت.
اقرن النافذة بالتدقيق وبفروق الحالة المرغوبة
محاولة محظورة لا يراها أحد نصف ضابط.
التدقيق. التدقيق على مستوى الكائنات والواعي لـ DDL هو كيف تُظهر أن محاولات خارج النافذة حدثت، ومن قام بها، وهل استُخدم كسر الزجاج. راجع مقالنا السابق عن سجل التدقيق المقاوم للتلاعب مع PGEE.
الفروق التصريحية. بعد إغلاق النافذة، فحوص المرغوب-مقابل-الواقع بأسلوب
permission_diffs()تلتقط صلاحيات رُفعت من أجل التغيير ولم تُخفض. نافذة صيانة تتركCREATEمعلّقًا على دور DBA بشري هي كيف تبدأ حادثة الخميس القادم.
CREATE EXTENSION pg_permissions SCHEMA public;-- Desired state: app role may DML on appschema tables, not DDL.INSERT INTO public.permission_target (role_name, permissions, object_type, schema_name)VALUES ('appuser', '{SELECT,INSERT,UPDATE,DELETE}', 'TABLE', 'appschema');SELECT * FROM public.permission_diffs();
المقتطف أعلاه هو واجهة pg_permissions مفتوحة المصدر. استخدمه كطبقة حالة مرغوبة قابلة للمراجعة. لا تقرأه كواجهة تعريف نوافذ PGEE.
قائمة تحقق للتجريب هذا الأسبوع
اسرد كل دور يستطيع تشغيل DDL على مخططات الإنتاج اليوم (
has_schema_privilegeوعضوية الأدوار وأي حساباتBYPASSRLS/ superuser). افترض أن ما يستطيع DDL عصر الثلاثاء سيفعله في النهاية.أعد مثال
table_rewriteالرسمي في عنقود غير إنتاجي. أكّد أنه يمنعALTER TABLEيعيد الكتابة خارج الساعات 1-6، ثم أكّد أنCLUSTERما زال يمر. هذه الثغرة الموثّقة.أضف محفّز
ddl_command_startبقائمةWHEN TAG IN (...)ضيّقة. جرّبCREATE ROLE/ALTER SYSTEMوسجّل ما *لا* يُمنع.كتمرين كسر زجاج، احذف محفّز الحدث بحساب superuser ولاحظ كم اختفى من «نافذتك». قرّر هل ينبغي أن يوجد هذا الدور كتسجيل دخول يومي.
أثناء تقييم PGEE، اسأل تحديدًا: كيف تُعرَّف نوافذ الصيانة في *بنائك*، وأي فئات كائنات تغطيها، وكيف يعمل كسر الزجاج، وهل يُقيَّد المستخدمون الفائقون (superusers) بالنافذة، وكيف تظهر انتقالات النافذة في سجل التدقيق. خذ نثر ملف PDF كنية. وخذ عنقود التقييم كحقيقة على الأرض.
تواصل مع Worlber
Worlber هي الموزّع المعتمد لـ CYBERTEC في المملكة العربية السعودية. نطبّق PGEE وندعمه من الرياض، بما في ذلك تصميم ضبط التغيير حول صلاحيات نوافذ الصيانة، وإعداد التدقيق، ومراجعات الصلاحيات التصريحية عبر pg_permissions.
أو راسلنا على contactus@worlber.com
المصادر
CYBERTEC، ملف Enterprise PostgreSQL Security v1.1 (2025-04-11)، قسم "RULE BASED MAINTENANCE PERMISSIONS": https://www.cybertec-postgresql.com/wp-content/uploads/2025/02/Enterprise-PostgreSQL-Security.pdf
مقارنة ميزات CYBERTEC PGEE ("Time related permissions"): https://www.cybertec-postgresql.com/en/cybertec-pgee-feature-comparison/
صفحة منتج CYBERTEC PGEE: https://www.cybertec-postgresql.com/en/products/cybertec-postgresql-enterprise-edition/
توثيق PostgreSQL، نظرة عامة على سلوك محفّزات الأحداث (نطاق
ddl_command_startوtable_rewrite): https://www.postgresql.org/docs/current/event-trigger-definition.htmlمرآة PostgreSQL / CYBERTEC، مثال محفّز حدث إعادة كتابة الجدول: https://repository.cybertec.at/doc/current/event-trigger-table-rewrite-example.html
CYBERTEC، pg_permissions (مفتوح المصدر): https://github.com/cybertec-postgresql/pg_permissions
Worlber، سجل تدقيق PostgreSQL مع PGEE: https://blog.worlber.sa/posts/postgresql-audit-logging-tamper-resistant-pgee
الخطوة التالية
تحدث مع مهندسي Worlber
أرسل لنا بيئتك وهدفك. سنوضح لك ما يصلح، وما لا يصلح، والخطوة الآمنة التالية.