سطر الأوامر psql أو mysql يكفي المطور الذي يعرف ما يريد، ولكن أغلب الفرق تحتاج في يوم ما واجهة في المتصفح: لترى جدولاً بسرعة، أو تصدر نتيجة استعلام لزميل في المحاسبة، أو تراقب الاتصالات المفتوحة، أو تعطي محللاً صلاحية القراءة دون أن تعلمه سطر الأوامر. والأدوات المفتوحة الأشهر لهذا ثلاث: pgAdmin لـ PostgreSQL، وAdminer الصغير الذي يعمل مع أغلب القواعد، وCloudBeaver النسخة الويب من DBeaver. والمشكلة أن هذه الواجهات هي أخطر شيء تستطيع أن تنشره على الإنترنت، لأن من يدخلها يملك قاعدة البيانات كلها، ولذلك سوف نبدأ بالأدوات ثم ننتهي بالطريقة الصحيحة لوضعها خلف باب واحد.
وسوف نناقش في هذا المقال ما يلي:
- pgAdmin وAdminer وCloudBeaver: ماذا يقدم كل واحد، ورخصته، ومتى يناسبك.
- تثبيت الثلاثة معاً بملف Docker Compose واحد على 127.0.0.1، وصور من تثبيتنا.
- مستخدم قاعدة بيانات بصلاحية القراءة فقط للواجهات وأدوات التقارير.
- لماذا لا تنشر هذه الواجهات على الإنترنت أبداً، وثغرات حقيقية فيها.
- الطريق الصحيح: VPN أو Reverse Proxy مع الدخول الموحد SSO عبر authentik.
الأدوات الثلاث باختصار
| pgAdmin 4 | Adminer | CloudBeaver Community | |
|---|---|---|---|
| القواعد | PostgreSQL فقط | MySQL وMariaDB وPostgreSQL وSQLite وMS SQL وOracle وغيرها بالإضافات | أغلب القواعد عبر مكتبات JDBC |
| المكتوب بها | Python وواجهة React | ملف PHP واحد | Java وواجهة ويب |
| الرخصة | PostgreSQL Licence | Apache 2.0 أو GPL 2.0 | Apache 2.0، وهناك نسخ تجارية |
| حجم ال Docker Image في التجربة | 791 MB | 164 MB | 1.14 GB |
| الذاكرة بعد الاستخدام | 240 MiB | 14 MiB | 317 MiB |
| المستخدمون | حسابات في pgAdmin نفسه، وLDAP وOAuth2 وKerberos ورأس Reverse Proxy | لا توجد حسابات، فالدخول باسم مستخدم قاعدة البيانات وكلمة مروره مباشرة | حسابات وصلاحيات على كل اتصال |
pgAdmin
pgAdmin هو الأداة الرسمية لمجتمع PostgreSQL، ويغطي كل شيء تقريباً: شجرة الكائنات، ومحرر الاستعلامات Query Tool مع خطة التنفيذ الرسومية EXPLAIN، ولوحة مراقبة للاتصالات والمعاملات، والنسخ الاحتياطي والاستعادة عبر pg_dump، وإدارة الأدوار والصلاحيات، ومقارنة المخططات Schema Diff. ويعمل في وضع السيرفر Server Mode بحسابات مستقلة لكل شخص، ويدعم الدخول عبر LDAP وOAuth2 كما في صفحة OAuth2، وهذا ما يجعله مناسباً للفرق.
نقاط القوة: أكمل أداة لـ PostgreSQL، وحسابات مستقلة ودخول موحد، ورخصة متساهلة، وتطوير نشط.
نقاط الضعف: PostgreSQL فقط، وأثقل من Adminer، والواجهة كثيرة التفاصيل لمن يريد جدولاً واحداً.
Adminer
Adminer ملف PHP واحد يدير MySQL وPostgreSQL وSQLite وغيرها، وفيه تصفح الجداول وتعديل الصفوف وتنفيذ SQL والتصدير والاستيراد. وهو أخف شيء ممكن، 14 MiB ذاكرة في التجربة. ولكن لاحظ أنه لا يملك حسابات خاصة به، فشاشة الدخول تطلب اسم السيرفر واسم المستخدم وكلمة المرور لقاعدة البيانات نفسها، وهذا يعني أن أي شخص يصل إلى صفحة Adminer يستطيع أن يجرب كلمات المرور على أي سيرفر قواعد بيانات يصل إليه Container، داخل شبكتك أيضاً.
نقاط القوة: خفيف جداً، ويدعم قواعد كثيرة، ولا يحتاج إعداداً، ومناسب لمهمة سريعة ثم الإيقاف.
نقاط الضعف: لا توجد حسابات ولا صلاحيات خاصة به، ولا توجد مراقبة أو أدوات إدارة متقدمة، وشاشة الدخول نفسها باب إلى كل سيرفر في الشبكة.
CloudBeaver
CloudBeaver هو DBeaver في المتصفح: محرر SQL مع إكمال تلقائي، ومخطط العلاقات ER Diagram، وتصفح البيانات وتعديلها، واتصالات بأغلب القواعد، وحسابات مع صلاحيات على كل اتصال، فتستطيع أن تعطي فريق التقارير اتصالاً بقاعدة واحدة فقط. ونسخة Community تحت Apache 2.0، والشركة تبيع نسخاً تجارية منها Enterprise وAWS، والتوثيق الرسمي مكتوب لهذه النسخ في الغالب، فتأكد أن الميزة التي تحتاجها، مثل طرق الدخول، موجودة في Community قبل أن تعتمد عليها.
نقاط القوة: واجهة حديثة وقريبة من DBeaver الذي يعرفه كثير من المطورين، وقواعد كثيرة، وصلاحيات على مستوى الاتصال.
نقاط الضعف: الأثقل بين الثلاثة (1.14 GB لل Docker Image)، والتوثيق يركز على النسخ التجارية، ومعالج الإعداد الأول مفتوح لأول زائر كما سوف نرى.
تجربة عملية: الثلاثة على قاعدة واحدة
سوف نشغل PostgreSQL 18.6 فيه قاعدة متجر صغيرة، ومعه pgAdmin 9.18.0 وAdminer 6.1.1 وCloudBeaver 26.2.2، وكل الواجهات مربوطة بالعنوان 127.0.0.1 فقط، على Docker Engine 29.8.0.
قاعدة المتجر ومستخدم القراءة فقط
الملف seed.sql يعمل عند أول تشغيل لـ PostgreSQL، وينشئ جدولين ومستخدماً اسمه report يقرأ فقط:
-- A small shop database for the screenshots (ArabRoot lab)
CREATE TABLE customers (id INT PRIMARY KEY, name TEXT NOT NULL, city TEXT NOT NULL);
CREATE TABLE orders (id BIGINT PRIMARY KEY, customer_id INT REFERENCES customers(id), status TEXT NOT NULL, amount NUMERIC(10,2) NOT NULL, created_at TIMESTAMP NOT NULL);
INSERT INTO customers SELECT n, 'customer ' || n, (ARRAY['Riyadh','Cairo','Dubai','Amman','Rabat'])[n % 5 + 1] FROM generate_series(1, 500) n;
INSERT INTO orders SELECT n, (n * 7) % 500 + 1, (ARRAY['paid','paid','paid','refunded','pending'])[n % 5 + 1], 5 + (n * 37 % 9000) / 10.0, TIMESTAMP '2026-01-01' + n * INTERVAL '17 minutes' FROM generate_series(1, 20000) n;
-- a read-only account for reporting and admin tools
CREATE ROLE report LOGIN PASSWORD 'report-lab-pass';
GRANT CONNECT ON DATABASE shop TO report;
GRANT USAGE ON SCHEMA public TO report;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO report;وشرحنا الأدوار والصلاحيات بالتفصيل في مقال PostgreSQL للفرق. والفكرة هنا أن الواجهة التي يستخدمها أغلب الناس لا تدخل بالمستخدم المالك للقاعدة، وإنما بمستخدم يقرأ فقط، فإذا أخطأ أحدهم بأمر DELETE أو سرقت كلمة المرور فالضرر محدود. وهذا ما حدث عندما جربنا الحذف بهذا المستخدم:
docker exec -e PGPASSWORD=report-lab-pass lab-dbui-db psql -h 127.0.0.1 -U report -d shop -c "SELECT count(*) FROM orders;" -c "DELETE FROM orders WHERE id = 1;" count
-------
20000
(1 row)
ERROR: permission denied for table ordersملف التشغيل
# pgAdmin, Adminer and CloudBeaver against one PostgreSQL, all on 127.0.0.1 only (ArabRoot lab).
# docker compose -p lab-dbui up -d
name: lab-dbui
services:
db:
image: postgres:18.6
container_name: lab-dbui-db
environment:
POSTGRES_USER: shop
POSTGRES_PASSWORD: shop-lab-pass
POSTGRES_DB: shop
volumes: ["./seed.sql:/docker-entrypoint-initdb.d/seed.sql:ro", "db-data:/var/lib/postgresql"]
pgadmin:
image: dpage/pgadmin4:9.18.0
container_name: lab-dbui-pgadmin
environment:
PGADMIN_DEFAULT_EMAIL: [email protected]
PGADMIN_DEFAULT_PASSWORD: pgadmin-lab-pass
PGADMIN_SERVER_JSON_FILE: /pgadmin4/servers.json
PGADMIN_LISTEN_ADDRESS: 0.0.0.0
volumes: ["./servers.json:/pgadmin4/servers.json:ro", "pgadmin-data:/var/lib/pgadmin"]
ports: ["127.0.0.1:15050:80"]
adminer:
image: adminer:6.1.1
container_name: lab-dbui-adminer
environment:
ADMINER_DEFAULT_SERVER: db
ports: ["127.0.0.1:15080:8080"]
cloudbeaver:
image: dbeaver/cloudbeaver:26.2.2
container_name: lab-dbui-cloudbeaver
environment: # skip the open setup wizard: the admin exists before the first visit
CB_SERVER_NAME: lab-cloudbeaver
CB_SERVER_URL: http://127.0.0.1:15978
CB_ADMIN_NAME: cbadmin
CB_ADMIN_PASSWORD: CloudBeaverLab1
volumes: ["cb-data:/opt/cloudbeaver/workspace"]
ports: ["127.0.0.1:15978:8978"]
volumes:
db-data:
pgadmin-data:
cb-data:والملف servers.json يسجل سيرفر القاعدة في pgAdmin مسبقاً، دون كلمة مرور:
{
"Servers": {
"1": { "Name": "shop (lab)", "Group": "Servers", "Host": "db", "Port": 5432, "MaintenanceDB": "shop", "Username": "shop", "SSLMode": "prefer" }
}
}في الإعداد أعلاه لاحظ التالي:
- كل المنافذ بالشكل
127.0.0.1:15050:80، فلا يصل إليها إلا من على نفس الجهاز، والسبب أن Docker عندما تكتب15050:80فقط يفتح المنفذ على كل عناوين السيرفر، ويتجاوز جدار الحماية في كثير من الإعدادات كما شرحنا في مقال شبكات Docker. - قاعدة البيانات نفسها بلا منفذ منشور أصلاً، والواجهات تصل إليها عبر شبكة Compose الداخلية.
- الإعداد
PGADMIN_LISTEN_ADDRESS: 0.0.0.0كان ضرورياً في تجربتنا، فالقيمة الافتراضية[::]لم تقبل الاتصال من المنفذ المنشور، وتوثيق pgAdmin في Docker يذكر أن البيئات التي تعمل بـ IPv4 فقط قد تحتاجه. - متغيرات
CB_ADMIN_NAMEوCB_ADMIN_PASSWORDتنشئ مدير CloudBeaver قبل أول زيارة، كما في صفحة إعداد CloudBeaver، وسوف نرى بعد قليل لماذا هي مهمة.
Adminer
شاشة الدخول تطلب نوع القاعدة والسيرفر واسم المستخدم وكلمة المرور، وهنا ندخل بالمستخدم report:

ولاحظ الرقم 6.1.2 بالأحمر بجانب الإصدار، فAdminer يتحقق من وجود إصدار أحدث ويخبرك به، وكان الإصدار 6.1.2 قد صدر قبل يوم واحد من التجربة. وبعد الدخول، هذه صفحة جدول الطلبات:

pgAdmin
pgAdmin يطلب حسابه هو أولاً، وهو الحساب الذي حددناه في PGADMIN_DEFAULT_EMAIL:

ثم عندما تفتح السيرفر المسجل يطلب كلمة مرور مستخدم قاعدة البيانات، لأننا لم نحفظها في servers.json:

ولا تقم بتحديد Save Password على سيرفر يدخله أكثر من شخص، والسبب أن من يصل إلى حساب pgAdmin يصل عندها إلى قاعدة البيانات دون أي كلمة مرور إضافية. وبعد الاتصال، هذا محرر الاستعلامات Query Tool مع نتيجة استعلام:

نكسرها: معالج CloudBeaver المفتوح لأول زائر
في أول تشغيل جربنا CloudBeaver دون متغيرات CB_ADMIN_*، وهذا ما ظهر لأول من فتح الصفحة:

والمشكلة واضحة: الصفحة لم تطلب أي شيء لتثبت أنك صاحب السيرفر، فإذا كان المنفذ منشوراً على الإنترنت، ولو لدقائق بين التشغيل وإكمال الإعداد، فأول زائر، وقد يكون ماسحاً آلياً، يصبح المدير. والحل هو ما في ال Compose أعلاه: أنشئ المدير بالمتغيرات قبل أول تشغيل. وبعد إعادة الإنشاء بهذه المتغيرات على Volume جديد لم يظهر المعالج، وظهرت الصفحة الرئيسية مباشرة:

ولاحظ أن الصفحة فتحت دون تسجيل دخول، وعندما سألنا واجهة GraphQL عن الإعداد كانت النتيجة كما يلي:
curl -s http://127.0.0.1:15978/api/gql -H 'Content-Type: application/json' -d '{"query":"{ serverConfig { anonymousAccessEnabled configurationMode version } }"}'{"data":{"serverConfig":{"anonymousAccessEnabled":true,"configurationMode":false,"version":"26.2.2.202610050917"}}}الدخول المجهول Anonymous Access كان مفعلاً في تجربتنا، مع أن صفحة الإعداد تذكر أن القيمة الافتراضية للمتغير CLOUDBEAVER_APP_ANONYMOUS_ACCESS_ENABLED هي false، والزائر المجهول هنا لا يرى أي اتصال لأننا لم نعطه شيئاً، ولكن أي اتصال تضيفه وتعطيه لفريق المجهولين يصبح مفتوحاً لكل من يصل إلى الصفحة. لذلك بعد التثبيت ادخل كمدير، وتأكد من إيقاف الدخول المجهول في إعدادات السيرفر، أو مرر المتغير صراحة، ثم اسأل الواجهة مرة أخرى كما فعلنا.
حذف المختبر
docker compose -p lab-dbui down -vوالصور أخذت بالسكربت shots.mjs في مجلد التجربة، وهو يستخدم متصفحاً بلا واجهة Headless على العناوين 127.0.0.1 نفسها.
لماذا لا تنشرها على الإنترنت أبداً؟
واجهة إدارة قاعدة البيانات تختلف عن أي تطبيق آخر تستضيفه، فهي مصممة لتعطي من يدخلها كل شيء: قراءة كل الجداول، والحذف، وتغيير الصلاحيات، وأحياناً تنفيذ أوامر على السيرفر. وهذه الواجهات نفسها كان فيها ثغرات خطيرة، وهذه ثلاث منها من قاعدة NVD:
- CVE-2025-2945 في pgAdmin قبل 9.2 بدرجة 9.9: تنفيذ كود عن بعد عبر نقطتين في محرر الاستعلامات ونشر السحابة كانتا تمرران مدخلات المستخدم إلى الدالة
eval()في Python. - CVE-2024-3116 في pgAdmin حتى 8.4 بدرجة 7.4: تنفيذ كود عن بعد عبر واجهة التحقق من مسار البرامج.
- CVE-2021-21311 في Adminer من 4.0.0 إلى ما قبل 4.7.9 بدرجة 7.2: تزوير الطلبات من جهة السيرفر SSRF، أي أن المهاجم يستخدم Adminer ليصل إلى عناوين داخل شبكتك.
والدرس ليس أن هذه الأدوات سيئة، فكلها أصلحت ثغراتها بسرعة، وإنما أن أي واجهة بهذه القوة سوف تظهر فيها ثغرة يوماً ما، والفرق بين يوم عادي وكارثة هو هل كانت الواجهة مكشوفة للإنترنت عندما ظهرت الثغرة أم لا. لذلك القاعدة: لا تقم بنشر واجهة إدارة قاعدة بيانات على الإنترنت، لا بكلمة مرور قوية ولا بمنفذ غير معروف، والسبب أن الماسحات الآلية تجد المنافذ غير المعروفة في دقائق، وكلمة المرور القوية لا تحميك من ثغرة قبل الدخول.
الطريق الصحيح: باب واحد قبل الواجهة
الصورة التالية تبين الفرق بين الطريقتين:

- الطريقة الأبسط لشخص واحد: اترك الواجهة على 127.0.0.1 في السيرفر، واصل إليها بنفق SSH من جهازك، مثل
ssh -L 15050:127.0.0.1:15050 user@serverثم افتحhttp://127.0.0.1:15050، كما شرحنا في قسم النفق المحلي في مقال الأنفاق. لا يوجد شيء منشور، ولا شيء تديره. - لفريق: شبكة خاصة VPN مثل WireGuard مع wg-easy، والواجهة تستمع على عنوان الشبكة الخاصة فقط، فمن ليس في الشبكة لا يرى المنفذ أصلاً.
- لفريق يريد الدخول من المتصفح بحساب الشركة: Reverse Proxy أمام الواجهة مع الدخول الموحد عبر authentik، باستخدام Forward Auth الذي يمنع الوصول إلى صفحة الواجهة نفسها قبل الدخول، فلا يرى المهاجم صفحة دخول pgAdmin أو Adminer أصلاً، ولا تصله أي ثغرة فيها قبل المصادقة. وpgAdmin يستطيع بعدها أن يقرأ اسم المستخدم من رأس Reverse Proxy أو أن يدخل بـ OAuth2 مباشرة.
وفي كل الحالات، الواجهة تدخل قاعدة البيانات بمستخدم محدود كما فعلنا مع report، وتستخدم المستخدم المالك فقط عندما تحتاج تعديل المخطط، وتوقف Adminer بعد المهمة إذا كان لمهمة واحدة، وتحدث الواجهات مع كل إصدار أمني.
أي واحد تختار؟
- فريق يستخدم PostgreSQL فقط: pgAdmin، بحسابات مستقلة ودخول موحد.
- مهمة سريعة على أي قاعدة، أو سيرفر صغير: Adminer، شغله عندما تحتاجه خلف نفق SSH ثم أوقفه.
- فريق بقواعد مختلفة (MySQL وPostgreSQL وغيرها) ويحب DBeaver: CloudBeaver، مع إنشاء المدير مسبقاً وإيقاف الدخول المجهول.
- تقارير ولوحات لغير المطورين: لا تعطهم واجهة إدارة، وإنما أداة تقارير مثل Apache Superset بمستخدم يقرأ فقط.
ولمن يريد أن يتعلم الاستعلامات نفسها التي سوف يكتبها في هذه الواجهات، فسلسلة SQL تبدأ من أساسيات SQL للمطورين.
أخطاء شائعة
- نشر المنفذ بالشكل
8080:8080: يفتحه Docker على كل العناوين، فاكتب127.0.0.1:8080:8080دائماً. - ترك معالج CloudBeaver دون إكمال على منفذ منشور: أول زائر يصبح المدير.
- الدخول من الواجهة بالمستخدم المالك أو postgres: خطأ واحد في المحرر يحذف جدولاً.
- حفظ كلمة مرور القاعدة في pgAdmin على سيرفر مشترك: من يملك حساب pgAdmin يملك القاعدة.
- الاعتماد على كلمة مرور قوية بدل الإخفاء: الثغرات قبل المصادقة لا تهتم بكلمة مرورك.
- ترك Adminer يعمل بعد المهمة: شاشة دخول مفتوحة إلى كل سيرفر في الشبكة.
الخلاصة
- pgAdmin الأكمل لـ PostgreSQL بحسابات ودخول موحد، وAdminer الأخف لأي قاعدة دون حسابات خاصة به، وCloudBeaver واجهة DBeaver في المتصفح بصلاحيات على كل اتصال.
- الرخص متساهلة: PostgreSQL Licence لـ pgAdmin، وApache 2.0 أو GPL 2.0 لـ Adminer، وApache 2.0 لـ CloudBeaver Community.
- في التجربة كان معالج CloudBeaver مفتوحاً لأول زائر، وكان الدخول المجهول مفعلاً، والمستخدم report رفض الحذف كما يجب.
- هذه الواجهات كان فيها ثغرات تنفيذ كود وSSRF، فلا تنشرها على الإنترنت أبداً.
- اجعلها على 127.0.0.1، وادخل إليها بنفق SSH أو VPN أو Reverse Proxy مع authentik، وبمستخدم قاعدة بيانات محدود.
دليلPostgreSQL للفرق: تثبيت آمن وصلاحيات واضحةكيف تثبت PostgreSQL لفريقك بأدوار لا تسمح للتطبيق بحذف الجداول، واتصال مشفر، ونسخة احتياطية تختبر استعادتها كل ليلة.
دليلكيف تحمي أي تطبيق ويب بصفحة دخول authentik عبر Forward Authكيف تضع صفحة دخول authentik مع المصادقة الثنائية أمام أي أداة داخلية دون أن تعدل في التطبيق نفسه.
دليلشبكة خاصة مع WireGuard وواجهة wg-easyكيف تنشئ شبكة VPN خاصة مع WireGuard وواجهة wg-easy، فلا يصل إلى لوحات الإدارة على سيرفرك إلا أجهزة فريقك.
دليلما هو النفق Tunnel في الشبكات؟ ومتى تحتاج إليهكيف تصل إلى قاعدة بيانات لا تراها من الإنترنت، وتنشر خدمة من خلف CGNAT دون فتح منافذ، ومتى تحتاج إلى VPN بدل النفق.
دليلتعلم SQL (1): ما هو SQL وكيف تكتب أول استعلامتتعلم SQL من الصفر: ما هي قاعدة البيانات العلائقية، وكيف تنشئ الجداول وتضيف البيانات وتكتب أول استعلام بأمان.سجل التحديثات
- أكتوبر 2026: كتابة المقال، والتجربة على pgAdmin 9.18.0 وAdminer 6.1.1 وCloudBeaver 26.2.2 مع PostgreSQL 18.6 في Docker Engine 29.8.0، والمعلومات من توثيق الأدوات الثلاث وقاعدة NVD كما قرأناها في 11 أكتوبر 2026.