พอร์ตที่เปิดทิ้งไว้แล้วลืม คือทางเข้าเซิร์ฟเวอร์แบบคลาสสิก: ฐานข้อมูลบนที่อยู่สาธารณะ แดชบอร์ดดีบัก หรือคอนเทนเนอร์ที่เผยแพร่พอร์ตซึ่งไม่มีใครตั้งใจ ถ้ารู้คำสั่งสองคำสั่งกับกับดักหนึ่งข้อ การตรวจใช้เวลาแค่นาทีเดียว เราทำทุกอย่างด้านล่างบนเซิร์ฟเวอร์ 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 resolver บน 127.0.0.53 เป็นส่วนตัว
หากต้องการแสดงเฉพาะสิ่งที่อินเทอร์เน็ตอาจมองเห็น ให้กรองที่อยู่ loopback ออก:
sudo ss -tlnp | awk 'NR>1 && $4 !~ /^(127\.|\[::1\])/ {print $4, $6}'
รันคำสั่งนี้ทุกครั้งหลังติดตั้งอะไรใหม่ เราติดตั้ง nginx, Docker และแอป Python เล็ก ๆ หนึ่งตัว รายการเพิ่มจาก 2 บรรทัดเป็น 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 และ worker ที่รันเป็น www-data ทั้งคู่อยู่บน *:80 สำหรับ IPv4 และ IPv6 ส่วน fuser แสดงเฉพาะ PID ซึ่งสะดวกในสคริปต์ หากต้องการปล่อยพอร์ต ให้หยุดบริการแทนการฆ่า PID ไม่เช่นนั้น 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 วินาที และพบพอร์ตเดียวเดิม พอร์ตที่ปิดจะตอบกลับด้วย reset ทันที การสแกนจึงเร็ว
มีสามสถานะที่สำคัญ:
- 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 บน Ubuntu รอรับบน localhost ตั้งแต่ติดตั้ง และคู่มือ 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 เป็น reverse proxy ไว้ด้านหน้าสำหรับฝั่งสาธารณะพร้อม 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 Firewall ทุกแพ็กเกจ Linux ตั้งแต่ Nano ราคา $3 ขึ้นไป มีรุ่นที่มีที่อยู่ IPv4 ของตัวเอง และที่นั่นสิ่งที่คุณเห็นใน ss ก็คือสิ่งที่ทั้งโลกเห็น ยกเว้นสิ่งที่ไฟร์วอลล์ของคุณกั้นไว้
ความคิดเห็น
ยังไม่มีความคิดเห็น เป็นคนแรกสิ