قائمة المنافذ المفتوحة هي قائمة السبل للوصول إلى خادمك. وكل ما عداها — جدران الحماية، وجدران حماية التطبيقات، وكشف التسلل — يُبنى فوقها. ولهذا يأتي سؤال «ما الذي يستمع هنا» أولًا في أي تدقيق، ولهذا أيضًا هو الفحص الذي يعطي إجابة مزعجة أكثر من غيره: فنصف ما يظهر لم تفتحه أنت بل مثبِّت حزمة ما.

قراءة المخرج

sudo ss -tulpn

الرايات: t لـ TCP، وu لـ UDP، وl لما يستمع فقط، وp للعملية، وn كي تبقى الأرقام أرقامًا. وبدون sudo يكون عمود العملية فارغًا ويفقد التمرين معناه.

المهم هو العمود Local Address:Port، والفرق هناك جوهري:

  • 127.0.0.1:3306 — الخدمة متاحة من الآلة نفسها فقط. وهذا جيد؛
  • 0.0.0.0:3306 — من أي عنوان IPv4، أي من الإنترنت. وهذا ما يحتاج فحصًا؛
  • [::]:3306 — الشيء نفسه لـ IPv6. سطر منفصل، ويُغفل بانتظام؛
  • 203.0.113.25:443 — على عنوان محدد، وعادةً عن قصد.

ثم اطرح لكل سطر فيه 0.0.0.0 أو [::] سؤالًا واحدًا: هل ينبغي لشخص غريب أن يصل إلى هنا. وللمنفذين 80 و443 الجواب نعم. ولكل ما عداهما تقريبًا الجواب لا.

النتائج المعتادة

Redis، المنفذ 6379. أخطر سطر هناك. فافتراضيًا لا يطلب Redis كلمة مرور، وأوامره تتيح كتابة ملف على القرص — أي مفتاح غريب في authorized_keys. وبين ظهور Redis على عنوان عام واستخدامه تمرّ ساعات، وأحيانًا أقل. تحقّق من وجود bind 127.0.0.1 وprotected-mode yes في الإعداد.

Memcached، 11211/UDP. حتى لو لم يكن في الداخل شيء ذو قيمة، يصير خادمك مضخّمًا لهجمات الآخرين — وستأتي الشكوى من مزوّدك أنت.

MySQL و PostgreSQL، 3306 و5432. ثمة كلمة مرور، لكن التخمين ضدها جارٍ باستمرار، وإصدارات قواعد البيانات تُحدَّث أقل مما ينبغي. وهي لا تحتاج إلى الإطلال خارجًا أبدًا تقريبًا: فالتطبيق يعيش على الآلة نفسها، ونفق SSH يكفي لعملك أنت.

Elasticsearch 9200 و MongoDB 27017. تاريخيًا بلا مصادقة افتراضيًا. ونسخهما العامة مصدر دائم لأخبار تسريب البيانات.

واجهة Docker البرمجية، 2375. منفذ تحكم Docker مفتوحًا يعني root على المضيف بلا أي كلمة مرور إطلاقًا. ويظهر عادة بعد تجارب مع الوصول البعيد إلى Docker.

لوحات التحكم و phpMyAdmin على منافذها الخاصة: 8080، 8083، 10000. لا نقول إنه لا يجوز فتحها أبدًا، لكنها بالضبط ما يجمع الحجم الأكبر من المحاولات.

أصلح الأمر عند الخدمة لا عند جدار الحماية

الرغبة في إغلاق كل نتيجة بقاعدة UFW مفهومة، لكن ذلك هو الخط الثاني لا الأول. فالقاعدة قد تُحذف خطأً، وجدار الحماية قد يُعطَّل مؤقتًا أثناء تتبّع الأخطاء، و Docker ينشر المنافذ متجاوزًا UFW تمامًا. أما إعداد الربط في ملف الخدمة نفسه فينجو من كل ذلك:

  • MySQL/MariaDB — bind-address = 127.0.0.1؛
  • PostgreSQL — listen_addresses = 'localhost'؛
  • Redis — bind 127.0.0.1 ::1؛
  • Docker Compose — انشر بصيغة "127.0.0.1:5432:5432".

ويأتي جدار الحماية فوق ذلك كتأمين، لا بديلًا عنه.

إيجاد العملية حين يكون الأمر غامضًا

يعطي ss الاسم ورقم العملية. ثم:

sudo systemctl status <pid>
sudo lsof -i :8080

يسمّي الأمر الأول وحدة systemd التي تنتمي إليها العملية — وهذا يكفي عادة لفهم ما هي وهل هي ضرورية. أما عملية مجهولة تستمع على منفذ عالٍ وأُطلقت من خارج أدلة النظام — من /tmp أو /dev/shm مثلًا — فلم تعد مسألة إعداد بل سببًا لتحقيق منفصل.

الفحص من الخارج إلزامي

يجيب ss عن «ما الذي يستمع» لا عن «ما الذي يمكن الوصول إليه». وبينهما يقف جدار الحماية و NAT وقواعد المزوّد نفسه. والإجابة الصادقة الوحيدة تأتي من مسح من آلة أخرى:

nmap -Pn -p- 203.0.113.25
nmap -Pn -p- -6 2001:db8::1

لا تتخطَّ الأمر الثاني: فلكل خادم VPS تقريبًا عنوان IPv6، وقواعده تُكتب على حدة، والخدمة تستمع على إصدارَي البروتوكول في آن واحد.

هذا ليس فحصًا لمرة واحدة

قائمة المنافذ المفتوحة تتغيّر من تلقاء نفسها. ثبّت حزمة فجلبت خدمة وفتحت منفذًا. حدّثت لوحة فأعادت قيمة افتراضية. أطلقت حاوية فنشرت منفذًا متجاوزة جدار الحماية. والفحص لمرة واحدة يجيب عن اليوم فحسب.

والقيمة ليست في القائمة نفسها بل في تغيّراتها: فمنفذ جديد لم يكن موجودًا أمس إشارة قصيرة وبالغة الدلالة. وهكذا بالضبط يستحق أن يُراقب — كلقطة مع تاريخ، لا كمخرج ss يُستدعى من الذاكرة. وصفحة العرض التوضيحي أدناه تُظهر ذلك بالذات.