المنفذ المفتوح والمنسيّ هو الطريق الكلاسيكي إلى داخل الخادم: قاعدة بيانات على عنوان عام، أو لوحة تصحيح أخطاء، أو حاوية نشرت منفذًا لم يرد أحد نشره. يستغرق الفحص دقيقة إذا عرفت أمرين وفخًّا واحدًا. نفّذنا كل ما يلي على خادم Ubuntu 24.04 جديد من صورتنا وفحصناه من جهاز ثانٍ، لذا فالمخرجات هي نفسها التي ستراها على خادمك.
1. ما الذي يستمع
الأمر هو ss. تعني الأحرف: TCP وUDP والمقابس المستمعة وأرقام المنافذ واسم العملية:
sudo ss -tulpn
على خادم جديد يطبع ستة أسطر:
Netid State Local Address:Port Process
udp UNCONN 127.0.0.54:53 systemd-resolve
udp UNCONN 127.0.0.53%lo:53 systemd-resolve
tcp LISTEN 127.0.0.54:53 systemd-resolve
tcp LISTEN 127.0.0.53%lo:53 systemd-resolve
tcp LISTEN 0.0.0.0:22 sshd
tcp LISTEN [::]:22 sshd
العمود المهم هو Local Address. يخبرك من يستطيع الاتصال:
| العنوان | من يصل إليه |
|---|---|
127.0.0.1:… أو 127.0.0.53 | البرامج على هذا الخادم فقط |
0.0.0.0:… | أي أحد عبر IPv4، ما لم يحجبه الجدار الناري |
[::]:… | أي أحد عبر IPv6 (وعادةً IPv4 أيضًا) |
10.0.0.5:… (عنوان محدد) | كل من يستطيع الوصول إلى ذلك العنوان |
إذن لا يكشف الخادم الجديد للخارج إلا شيئًا واحدًا: SSH على المنفذ 22 عبر IPv4 وIPv6. أما محلّل DNS على 127.0.0.53 فخاص.
لعرض ما قد يراه الإنترنت فقط، استبعد عناوين loopback:
sudo ss -tlnp | awk 'NR>1 && $4 !~ /^(127\.|\[::1\])/ {print $4, $6}'
شغّل هذا بعد كل تثبيت. ثبّتنا nginx وDocker وتطبيق Python صغيرًا، فكبرت القائمة من سطرين إلى 10 أسطر، منها docker-proxy على 0.0.0.0:8081 ومقبس containerd على منفذ localhost عشوائي.
2. أي عملية تشغل المنفذ
عندما يكون المنفذ مشغولًا وتريد معرفة من يشغله:
sudo lsof -i :80 -P -n
sudo fuser 80/tcp
sudo ss -tlnp 'sport = :80'
الأسهل قراءة هو lsof. أظهر لـ nginx عملية رئيسية باسم root وعاملًا باسم www-data، كلاهما على *:80 لكل من IPv4 وIPv6. أما fuser فيطبع أرقام العمليات فقط، وهو مفيد في السكربتات. إذا احتجت إلى تحرير المنفذ فأوقف الخدمة بدل قتل العملية، وإلا أعاد systemd تشغيلها: sudo systemctl stop nginx.
3. ما الذي يراه الإنترنت
يعرض ss منظور الخادم. وقد يختلف منظور الإنترنت، لأن بينهما جدارًا ناريًا أو لأن الخدمة مربوطة بـ 127.0.0.1. لرؤية الصورة الحقيقية افحص الخادم من جهاز آخر، كحاسوبك المحمول أو خادم ثانٍ. الأداة هي nmap (apt install nmap، أو المثبّت من nmap.org على Windows وmacOS).
nmap your-server-ip
nmap -p- your-server-ip
nmap -p 22,80,443,3306 your-server-ip
الصيغة الأولى تفحص أكثر 1,000 منفذ شيوعًا، والثانية كل المنافذ الـ 65,535، والثالثة قائمة محددة. على خادمنا الجديد قبل أي جدار ناري:
Not shown: 999 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
Nmap done: 1 IP address (1 host up) scanned in 6.83 seconds
استغرق الفحص الكامل للمنافذ الـ 65,535 مدة 8.9 ثانية ووجد المنفذ الوحيد نفسه. المنافذ المغلقة ترد فورًا بإعادة ضبط، ولهذا يكون فحصها سريعًا.
ثلاث حالات مهمة:
- open: قبل برنامج ما الاتصال. هذا هو سطح الهجوم لديك.
- closed: ردّ الخادم بأن لا شيء يستمع هناك. آمن، لكنه يؤكد أن الخادم موجود وما الذي لا يشغّله.
- filtered: لا رد. أسقط جدار ناري الحزمة. لا يستطيع الماسح معرفة إن كانت خلفه خدمة.
لمنفذ واحد دون تثبيت أي شيء، يُبلغ nc -zv عنوان-الخادم 443 بـ succeeded أو refused، ويبقى معلّقًا حتى انتهاء المهلة على المنفذ المُرشَّح. أما UDP فأضف -sU إلى nmap وتوقّع البطء؛ على خادمنا كانت منافذ UDP الشائعة الثلاثة 53 و123 و161 كلها closed.
ملاحظة عن خطط NAT: لا يصل من الإنترنت إلا منفذ SSH الذي حوّلناه إليك. فحص العنوان المشترك يُظهر حافة شبكتنا لا خادمك، وبقية المنافذ غير قابلة للوصول أيًّا كان ما يستمع عليها. في خطة بعنوان IPv4 خاص تدير أنت كل منفذ مفتوح.
4. أغلق ما لا تحتاجه
ثلاث طرق، من الأفضل إلى الأوسع.
اربط الخدمة بـ localhost. إذا كانت البرامج على الخادم وحدها تستخدم خدمة ما، فاجعلها تستمع على 127.0.0.1. شغّلنا تطبيق ويب صغيرًا بـ --bind 127.0.0.1 8080: أظهره ss على 127.0.0.1:8080، وأبلغ nmap من الخارج عن 8080/tcp closed. هذا هو الإعداد الافتراضي الصحيح لقواعد البيانات: Redis وMySQL وPostgreSQL تستمع على localhost مباشرة بعد التثبيت على Ubuntu، ودليلانا لـ Redis وMySQL يُبقيانها كذلك.
أوقف الخدمة. يزيل sudo systemctl disable --now الاسم المستمع نهائيًا. تحقق بـ ss من اختفائه.
شغّل الجدار الناري. اسمح بـ SSH أولًا، ثم بما تقدّمه، ثم فعّله:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw enable
sudo ufw status verbose
تغيّر الفحص من الخارج تمامًا:
Not shown: 997 filtered tcp ports (no-response)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
8081/tcp open blackice-icecap
Nmap done: 1 IP address (1 host up) scanned in 10.18 seconds
انتقل كل منفذ غير مسموح به من closed إلى filtered، وأصبح الفحص الكامل للمنافذ الـ 65,535 يستغرق 110 ثوانٍ بدل 8.9. وقت أطول لكل منفذ لدى الماسح، ومعلومات أقل عن خادمك. يشرح دليل UFW لدينا تحديد المعدل والسماح لعنوان IP واحد وIPv6.
لكن انظر إلى السطر الثالث.
5. مفاجأة Docker
بقي المنفذ 8081 مفتوحًا مع تشغيل الجدار الناري ودون أي قاعدة تسمح به. هذا المنفذ يخص حاوية شغّلناها بـ docker run -p 8081:80 nginx:alpine. يضيف Docker قواعده الخاصة إلى جدار النواة الناري، وتُقيَّم هذه القواعد قبل قواعد ufw. القاعدة التي كتبها على خادمنا:
-A DOCKER ! -i docker0 -p tcp --dport 8081 -j DNAT --to-destination 172.17.0.2:80
كل حزمة متجهة إلى المنفذ 8081 تُحوَّل إلى الحاوية قبل أن تتاح لـ ufw كلمة. تفعل ذلك كل حاوية فيها -p أو مدخل ports:، ويعرضها ss بأمانة على أنها docker-proxy على 0.0.0.0:8081.
الحل هو النشر على localhost فقط:
docker rm -f web
docker run -d --name web -p 127.0.0.1:8081:80 nginx:alpine
في Compose اكتب "127.0.0.1:8081:80" تحت ports:. بعد التغيير أظهر ss العنوان 127.0.0.1:8081، واكتسبت قاعدة Docker الجزء -d 127.0.0.1/32، وأبلغ الفحص من الخارج عن 8081/tcp filtered. ضع nginx كوكيل عكسي في الأمام للجانب العام مع HTTPS، كي تكون المنافذ المفتوحة الوحيدة على الخادم هي 22 و80 و443.
قائمة تحقق بعد كل تغيير
sudo ss -tulpn: هل كل سطر فيه0.0.0.0أو[::]شيء أردت كشفه؟nmap عنوان-الخادممن حاسوبك المحمول: هل تتطابق المنافذ المفتوحة؟- قاعدة بيانات أو ذاكرة تخزين مؤقت أو لوحة إدارة على عنوان عام: انقلها اليوم إلى
127.0.0.1أو خلف الجدار الناري. - كل مدخل
ports:في Docker: هل يبدأ بـ127.0.0.1:إن لم يكن منفذ الويب العام؟ - SSH مفتوح للجميع: أضف fail2ban أو تسجيل الدخول بالمفتاح فقط.
على Windows Server يكون الفحص نفسه بالأمر Get-NetTCPConnection -State Listen، وتوجد القواعد في جدار Windows الناري. لكل خطة Linux، بدءًا من Nano بسعر $3، نسخة بعنوان IPv4 خاص، وهناك يرى العالم كله ما تراه في ss، باستثناء ما يحجبه جدارك الناري.
التعليقات
لا تعليقات بعد. كن الأول.