Danh sách cổng mở là danh sách các đường tới máy chủ của bạn. Mọi thứ còn lại — tường lửa, WAF, phát hiện xâm nhập — đều xây trên đó. Vì thế «cái gì đang lắng nghe ở đây» luôn đứng đầu trong mọi cuộc rà soát, và cũng vì thế đây là phép kiểm tra cho câu trả lời khó chịu thường xuyên nhất: một nửa những gì tìm thấy không phải do bạn mở mà do trình cài đặt của một gói nào đó.

Đọc kết quả

sudo ss -tulpn

Các cờ: t là TCP, u là UDP, l là chỉ những cái đang lắng nghe, p là tiến trình, n là để số cứ là số. Không có sudo thì cột tiến trình trống rỗng và cả bài tập mất ý nghĩa.

Điều quan trọng là cột Local Address:Port, và khác biệt ở đó mang tính nền tảng:

  • 127.0.0.1:3306 — dịch vụ chỉ với tới được từ chính máy đó. Thế là tốt;
  • 0.0.0.0:3306 — từ mọi địa chỉ IPv4, tức là từ Internet. Đây là thứ cần kiểm tra;
  • [::]:3306 — điều tương tự với IPv6. Một dòng riêng, và thường xuyên bị bỏ sót;
  • 203.0.113.25:443 — trên một địa chỉ cụ thể, thường là có chủ đích.

Sau đó với mỗi dòng có 0.0.0.0 hoặc [::], hãy đặt một câu hỏi: một người lạ có nên với tới được cái này không. Với 80 và 443 câu trả lời là có. Với gần như mọi thứ còn lại là không.

Những phát hiện thường gặp

Redis, cổng 6379. Dòng nguy hiểm nhất ở đó. Mặc định Redis không đòi mật khẩu, và các lệnh của nó cho phép ghi tệp xuống đĩa — tức là một khóa lạ trong authorized_keys. Giữa lúc Redis xuất hiện trên địa chỉ công khai và lúc nó bị lợi dụng là vài giờ, đôi khi ít hơn. Hãy kiểm tra bind 127.0.0.1protected-mode yes trong tệp cấu hình.

Memcached, 11211/UDP. Ngay cả khi bên trong chẳng có gì giá trị, máy chủ của bạn vẫn trở thành bộ khuếch đại cho các cuộc tấn công của người khác — và khiếu nại sẽ đến từ nhà cung cấp của bạn.

MySQL và PostgreSQL, 3306 và 5432. Có mật khẩu thật, nhưng việc dò nó diễn ra liên tục, còn phiên bản cơ sở dữ liệu thì được cập nhật ít hơn mong muốn. Chúng gần như không bao giờ cần hướng ra ngoài: ứng dụng nằm trên cùng máy, còn cho công việc của bạn thì một đường hầm SSH là đủ.

Elasticsearch 9200, MongoDB 27017. Về mặt lịch sử là không có xác thực theo mặc định. Các phiên bản công khai của chúng là nguồn tin rò rỉ dữ liệu thường trực.

Docker API, 2375. Một cổng điều khiển Docker đang mở chính là quyền root trên máy chủ mà không cần bất kỳ mật khẩu nào. Nó thường xuất hiện sau các thử nghiệm truy cập Docker từ xa.

Bảng điều khiển và phpMyAdmin trên cổng riêng của chúng: 8080, 8083, 10000. Không phải là không bao giờ được mở, nhưng chính chúng gom về phần lớn các lần thử.

Hãy sửa ở dịch vụ, không phải ở tường lửa

Ham muốn đóng mọi phát hiện bằng một quy tắc UFW là dễ hiểu, nhưng đó là tuyến thứ hai chứ không phải tuyến đầu. Quy tắc có thể bị xóa nhầm, tường lửa có thể bị tắt tạm khi gỡ lỗi, còn Docker thì xuất bản cổng vòng hẳn qua UFW. Cấu hình gắn địa chỉ trong chính tệp cấu hình của dịch vụ sống sót qua tất cả những thứ đó:

  • MySQL/MariaDB — bind-address = 127.0.0.1;
  • PostgreSQL — listen_addresses = 'localhost';
  • Redis — bind 127.0.0.1 ::1;
  • Docker Compose — xuất bản dạng "127.0.0.1:5432:5432".

Tường lửa đặt lên trên như một lớp bảo hiểm, chứ không thay thế điều này.

Tìm tiến trình khi không rõ ràng

ss cho tên và pid. Sau đó:

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

Lệnh đầu cho biết tiến trình thuộc unit systemd nào — thường là đủ để hiểu nó là gì và có cần hay không. Một tiến trình lạ lắng nghe trên cổng cao và khởi chạy từ ngoài các thư mục hệ thống — chẳng hạn từ /tmp hay /dev/shm — không còn là câu hỏi về cấu hình nữa mà là cơ sở cho một cuộc điều tra riêng.

Kiểm tra từ bên ngoài là bắt buộc

ss trả lời «cái gì đang lắng nghe», chứ không phải «có thể với tới cái gì». Giữa hai điều đó là tường lửa, NAT và các quy tắc của chính nhà cung cấp. Câu trả lời trung thực chỉ có được khi quét từ một máy khác:

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

Đừng bỏ qua lệnh thứ hai: gần như mọi VPS đều có địa chỉ IPv6, quy tắc của nó được viết riêng, còn dịch vụ thì lắng nghe trên cả hai phiên bản giao thức cùng lúc.

Đây không phải phép kiểm tra một lần

Danh sách cổng mở tự nó thay đổi. Bạn cài một gói và nó kéo theo một dịch vụ rồi mở một cổng. Bạn cập nhật một bảng điều khiển và nó khôi phục giá trị mặc định. Bạn chạy một container và nó xuất bản cổng vòng qua tường lửa. Một lần kiểm tra chỉ trả lời cho hôm nay, không hơn.

Giá trị không nằm ở bản thân danh sách mà ở những thay đổi của nó: một cổng mới hôm qua chưa có là tín hiệu ngắn gọn và rất giàu thông tin. Chính theo cách đó mới nên theo dõi nó — như một ảnh chụp có lịch sử, chứ không phải kết quả ss nhớ lại trong đầu. Trang demo bên dưới cho thấy đúng điều đó.