服务器暴露面自测绘实战:用 nmap 从公网扫出被遗忘的开放端口,别让调试服务裸奔到线上

为什么你的服务器需要一次"外部视角"的体检

很多站长对自己的服务器了如指掌:装了 Nginx、跑了 MySQL、开了 SSH。但如果你站在公网攻击者的角度看同一台机器,画面可能完全不同。你可能在几个月前调试某个功能时临时起了一个 Redis、装过一次 phpMyAdmin 忘了关、给某个测试脚本开了个 8080 端口,之后再也没有回头清理。这些"遗忘的入口"自己从不主动提醒你,却会安安静静地挂在公网上,等着被扫描器找到。

这就是"暴露面"(attack surface)的含义:一台服务器上所有从外部可以访问的端口、服务与路径的集合。它的可怕之处在于,你本地执行 ss -tlnp 看到的是"进程想监听什么",而公网扫描器看到的是"防火墙和云安全组允许什么真正穿出去"。这两者经常对不上——而这中间的差值,就是真正的风险。

这篇文章讲的就是一件事:用 nmap 从公网视角给自己的服务器做一次端口与服务测绘,把"我以为开放的"和"实际开放的"对齐。整套流程不需要 root、不需要装 agent,一台能上网的机器就能做完。

第一步:先理清楚"应该开放"的清单

在扫描之前,你必须先写下一份"预期开放清单"。没有基准,扫描结果就没法判断。个人站长的典型预期通常是:

22/tcp   SSH(最好已换成密钥登录 + 非默认端口)
80/tcp   HTTP(跳转 HTTPS,或直接 301)
443/tcp  HTTPS
其它一律不应该对公网开放

注意这里的两个常见误区。第一,MySQL 的 3306、Redis 的 6379、Elasticsearch 的 9200 这些服务,正确的姿势是绑 127.0.0.1 或者只监听内网地址,而不是"监听 0.0.0.0 然后用防火墙挡"。因为防火墙规则会被误改、被云平台覆盖、被 Docker 的 iptables 链绕过(这一点后面细讲),而绑定回环是内核层面的硬隔离,绕不过去。第二,面板类服务(宝塔、Cockpit、Adminer、phpMyAdmin)属于"绝对不该裸奔在公网"的一类,正确做法是绑回环 + SSH 隧道访问。

把这份清单写成一个文件,比如 expected-ports.txt,后面要和扫描结果做 diff。

第二步:从一台"外部机器"发起扫描

关键点:扫描必须从公网发起,不能从服务器本机发起。本机扫自己会走 loopback,绕过了所有防火墙和云安全组规则,扫出来的全是"监听状态"而非"真实可达状态",毫无意义。正确做法是找一台不同网络环境的机器:你自己的笔记本、另一台 VPS、或者任何一个能连公网的临时主机。

装好 nmap 后,先做一次快速的 TCP 全端口扫描,找出"哪些端口是开着的":

nmap -Pn -p- --min-rate 2000 -T4 你的服务器IP

参数含义要讲清楚,否则很容易误用:

  • -Pn:跳过主机存活探测。很多云服务器默认禁 ping,不加这个参数 nmap 会认为主机不在线而直接放弃,你会得到一份"全 filtered"的假结果。
  • -p-:扫描全部 65535 个端口,而不是默认的 1000 个常用端口。遗忘的服务往往开在非常规端口上,只扫常用端口等于自欺欺人。
  • --min-rate 2000:强制每秒至少发 2000 个包,加快扫描。不加的话全端口扫描可能要跑十几分钟。
  • -T4:时序模板,4 是"较激进",适合自己的服务器。别用 -T5,容易触发目标或中间网络的限速丢包。

扫描结果里,open 是真正对外开放的,filtered 表示被防火墙静默丢弃(看不到也不回包),closed 表示端口没服务但主机回应了 RST。对站长来说,只关心 open 的那几个。

第三步:对开放端口做服务指纹识别

知道"22 开着"还不够,你还想知道"22 上跑的到底是什么版本、有没有已知漏洞"。对第一步扫出来的开放端口做一次带版本探测的细致扫描:

nmap -Pn -sV -sC -p 22,80,443,8080 你的服务器IP
  • -sV:版本探测,尝试识别每个端口上服务的具体名称和版本号(比如 OpenSSH 8.4p1、nginx 1.24.0)。
  • -sC:运行 nmap 默认脚本集(NSE 的 default 类别)。它会帮你查一些常见信息,比如 HTTP 标题、SSH 主机密钥指纹、SSL 证书内容,甚至发现某些服务配置了匿名访问。

看到 -sV 报出的版本号后,去核对是否使用了仍在维护的分支。比如某个老系统中残留的 OpenSSH 7.x、早已 EOL 的 PHP 5.6 或 MySQL 5.5,就是典型的高风险信号。

第四步:把扫描结果和预期清单做差异分析

这是整件事最有价值的一步。把 open 的端口列表和你第一步写的 expected-ports.txt 一对比,差异分三种,处理方式完全不同:

① 预期内、且服务正常的——比如 80/443 是 Nginx,22 是 OpenSSH。核对一下版本不过旧即可。

② 预期外、但其实是合理的——比如你确实在跑一个 HTTPS 的管理后台在 8443,或者做邮件服务器的 25/465/587。这类需要补进预期清单,并确认它本身有认证、有访问控制。

③ 预期外、你自己都不知道的——这就是真正的敌人。常见来源包括:调试时临时开的服务忘了关、某个 Docker 容器用 -p 把内部端口暴露到了公网、软件默认监听 0.0.0.0、面板/数据库装完没改绑定。这类端口必须立刻处理:要么关掉,要么改成只监听回环,要么最小化到只对可信 IP 开放。

第五步:收口——把不该开的口子关掉

发现多余的开放端口后,按"由内到外"的顺序逐层收口:

最内层:改服务绑定地址。这是根因层。以 Redis 为例,编辑 redis.conf:

bind 127.0.0.1 -::1
protected-mode yes

MySQL 则在 my.cnf 里写 bind-address = 127.0.0.1。改完重启服务,再扫一次,端口应该从 open 变成 closed(本机还在监听,但公网不可达)。

中间层:系统防火墙。用 nftables 或 ufw 只放行必要端口。以 ufw 为例:

ufw default deny incoming
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

最外层:云安全组。云平台的入站规则是最后一道也是最容易被忽略的闸。很多"我明明关了但还是通"的情况,都是宿主机防火墙、云安全组、Docker iptables 三套规则打架的结果。

必须讲清楚的那个坑:Docker 会绕过你的防火墙

这是站长最容易踩、也最反直觉的一个陷阱。当你在 docker run 里写 -p 8080:80 时,Docker 默认会在 iptables 的 DOCKER 链里插入一条 DNAT 规则,这条规则在 ufw/nftables 的过滤规则之前生效。结果是:你明明用 ufw 拦了 8080,公网却依然能访问到容器里的服务。ufw 的 deny 对 Docker 发布端口基本形同虚设。

正确的收口方式有两条路:

路一(推荐):不要用 -p,改成只绑回环。

docker run -d -p 127.0.0.1:8080:80 your-image

这样端口只在本机可达,再配合 Nginx 反代对外提供服务,攻击面就收到 Nginx 一个入口上。

路二:修改 Docker 的 iptables 行为。在 /etc/docker/daemon.json 里加 "iptables": false,让 Docker 不再自己插规则(代价是容器网络需要你自己接管,新手不推荐)。

判断有没有踩这个坑,扫一次就知道:如果 ss -tlnp 看端口绑的是 0.0.0.0:8080 而不是 127.0.0.1:8080,而你并没有打算对外暴露它,那基本就是被 Docker 默认行为坑了。

把测绘变成例行公事

暴露面不是一次扫描就一劳永逸的——你今天关掉的端口,可能下周调试时又被自己打开了。建议做成一个每月跑一次的小流程:

  1. 从外部机器执行 nmap -Pn -p- --min-rate 2000 -T4 IP,把结果存档。
  2. 和上个月的档对比,看有没有新增的开放端口。新增的永远是最高优先级。
  3. 对新增端口判断来源,收口或补进预期清单。
  4. 顺便核对一次 -sV 版本,看有没有该升级的服务。

更进一步,你可以只在扫描发现异常时收到通知:把两次结果 diff 一下,有差异就发邮件或推送到 Telegram,这样平时完全不用惦记它。

一键自带笔记:扫描命令速查

# 1. 快速全端口(先知道有哪些是开的)
nmap -Pn -p- --min-rate 2000 -T4 IP

# 2. 服务与版本指纹(只扫确认开放的端口)
nmap -Pn -sV -sC -p 22,80,443 IP

# 3. 顺带看一眼 SSL 证书与 HTTP 标题
nmap -Pn -p 443 --script ssl-cert,http-title IP

# 4. 对比两次结果,只输出新增开放端口
diff -u old.txt new.txt

写在最后

很多人以为安全加固就是"把密码改复杂点""装上 fail2ban"。这些固然有用,但它们防的是"已知入口上的攻击"。而暴露面测绘解决的是更前置的问题:先搞清楚你到底有几个入口。一个自己都忘了的 8080 端口背后可能是一个三个月没更新的面板,密码还是安装时生成的弱口令——这种口子,任何针对 22 端口的加固都保护不了你。

所以,别只从服务器内部看自己。换一双公网的眼睛,把"我以为的"和"实际暴露的"对齐。这件事花不了半小时,但它可能是你这一年里性价比最高的一次安全投入。

Last modification:October 6th, 2026 at 07:25 pm

Leave a Comment