服务器端口安全怎么设置

结论: 服务器端口安全的核心不是"怎么开放更多端口",而是"用最小开放面提供全部服务"——只放行业务必需的端口,管理类端口只对内网或固定 IP 放通,数据库与缓存中间件的默认端口绝不对公网,并用 iptables/firewalld 的默认 DROP 策略兜底,再用外部 nmap 验证收敛结果。

如果你想知道的是"怎么把一个端口放通",那是开放操作,请见 服务器端口怎么开放。本文讨论的是相反方向的命题:服务器上每一个对外开放的端口,都是一个可被枚举、可被扫描、可被利用的入口,安全治理的目标是让这个集合尽量小、每条条目都可以解释清楚"为什么必须开"。

为什么端口越少越安全?理解暴露面

攻击面(Attack Surface)是指攻击者能与之交互的所有外部接口的集合。对一台服务器来说,最主要的构成部分就是开放端口与其背后监听的服务。一个开放端口意味着:服务版本的指纹可被识别(nmap -sV)、已知漏洞可被匹配利用、弱口令可被爆破、默认配置可被直接访问。

端口与现实损失的对应关系可以量化:

  • 端口数越多,自动化扫描器命中可利用服务的概率越高。一台开放 22/3306/6379 的机器,平均存活时间以小时计。
  • 即使是"不知道自己在哪儿做了什么"的开发环境端口(如 8080 的 Tomcat Manager、9000 的 PHP-FPM),也常被直接在公网访问。php-fpm 未授权访问可以直接导致远程代码执行。
  • 每个开放端口还需要长期的补丁维护责任。开放却无人维护的服务是最危险的资产。

因此端口安全治理遵循三条原则:默认拒绝(Default Deny)、按需放行(Least Exposure)、来源限制(Source Restriction)。

高危端口清单与处置建议

下表列出在生产服务器上最常被误开放的端口,以及它们的推荐处置方式:

端口服务主要风险推荐处置
22SSH暴力破解、弱密钥密钥登录 + fail2ban,仅放通固定 IP 或 VPN
23Telnet明文传输,凭据可被嗅探立即关闭,一律改用 SSH
25SMTP被滥用作垃圾邮件中继不出邮件就关闭;出站也应限制
111 / 2049rpcbind / NFS未授权读取共享目录绑定内网,禁止公网
135~139 / 445Windows NetBIOS / SMB勒索病毒横向传播主通道立即关闭,云安全组也要封堵
1433MS SQL Server弱口令爆破、提权仅内网,改非默认端口 + 强口令
1521Oracle弱口令、默认账户仅内网访问
3306MySQL数据被拖库、勒索加密仅内网 / 127.0.0.1,永不公网
3389RDP暴力破解、蓝屏漏洞改端口 + 网络级认证 + 仅 VPN
5432PostgreSQL未授权访问、拖库仅内网,pg_hba.conf 限定来源
6379Redis无认证可写公钥直接 Getshell绝不对公网,必须设密码 + rename-command
9200 / 9300Elasticsearch无认证可读写全部索引绝不对公网,开启 x-pack 安全模块
11211Memcached可被用于 UDP 反射放大攻击关闭 UDP,绑定内网
2181ZooKeeper未授权获取集群信息仅内网,开 SASL 认证
2375 / 2376Docker Daemon未授权等于拿到宿主机 root立即关闭,改走本地 socket
27017MongoDB无认证导致勒索删库仅内网,开启 auth
8080 / 8888Web 管理后台、宝塔面板弱口令直接进后门限制来源 IP,改端口,加二次验证

这张表里最有价值的一条经验是:结尾是"数据库/缓存中间件"的端口,99% 的情况下都不应该出现在公网。

第一步:自查服务器到底开了哪些端口

排查要从服务器内部看真实监听情况。ss 是 netstat 的现代替代品,直接读取内核 socket 表,速度快得多。

bash
# 查看所有监听端口,显示进程名与 PID(最关键的一条命令)
ss -lntup

# -l 仅监听  -n 不解析域名(快)  -t TCP  -u UDP  -p 显示进程
# 只看 TCP 监听
ss -lnt
# 只看 IPv4
ss -lntup -4
# 查看已建立的连接数与来源
ss -tnp state established

# 输出示例解读:
# LISTEN 0  128  0.0.0.0:3306  0.0.0.0:*  users:(("mysqld",pid=1024,fd=30))
#                 ^^^^^^ 监听所有网卡 = 公网可达,需要收敛
# LISTEN 0  128  127.0.0.1:6379 0.0.0.0:* users:(("redis-server",pid=888,fd=6))
#                 ^^^^^^^ 只监听回环 = 安全

重点看第一列地址:0.0.0.0:端口 或 *:端口 表示监听所有网卡,公网可达;127.0.0.1:端口 表示只有本机可访问,是安全状态。把每个 0.0.0.0 的条目都问一遍:"这个端口真的需要对公网吗?"

如果某个端口不知道是哪个进程占用的:

bash
# 按端口反查进程
ss -lntup | grep -w 9000
# 或
lsof -i :9000
# 或(需 root)
fuser -v 9000/tcp

# 端口被占用想释放时,不要直接 kill -9,先看是谁
systemctl status $(ss -lntup | awk '/:9000 /{print $NF}' | sed 's/users:(("//;s/".*//')

第二步:端口收敛的四种手段

端口收敛的具体方法按推荐优先级排列:

手段一:停掉不需要的服务(最彻底)

bash
# 找出所有开机自启的服务
systemctl list-unit-files --type=service --state=enabled

# 停用并禁止开机自启(以 rpcbind 为例)
systemctl disable --now rpcbind.socket rpcbind

# 彻底卸载更干净
apt purge -y rpcbind      # Debian 系
# dnf remove -y rpcbind   # RHEL 系

手段二:把服务绑定到内网/回环地址

conf
# Redis /etc/redis/redis.conf
bind 127.0.0.1 10.0.0.12
protected-mode yes
requirepass Str0ng!Pass#2026
rename-command FLUSHALL ""
rename-command CONFIG ""

# MySQL /etc/mysql/mysql.conf.d/mysqld.cnf
bind-address = 10.0.0.12
skip-networking = 0

# Nginx 后台管理端口只允许内网
server {
    listen 10.0.0.12:8080;
    allow 10.0.0.0/8;
    allow 203.0.113.5;
    deny all;
    ...
}

手段三:防火墙按来源限制(推荐)

bash
# firewalld rich rule:SSH 只对办公室 IP 开放,MySQL 只对内网开放
firewall-cmd --permanent --remove-service=ssh
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.5/32" service name="ssh" accept'
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/8" port port="3306" protocol="tcp" accept'
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
firewall-cmd --list-all

手段四:iptables 默认 DROP 策略(兜底)

默认放行(ACCEPT)的防火墙形同虚设。正确姿势是:INPUT 链默认 DROP,只显式放行必要条目。

bash
# 先清空再重建(务必保留当前 SSH 连接,并在控制台有备用通道时执行)
iptables -F && iptables -X
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# 放行本地回环,否则很多本地服务会异常
iptables -A INPUT -i lo -j ACCEPT
# 放行已建立的和相关的连接(关键,否则出去的请求回不来)
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 放行业务端口
iptables -A INPUT -p tcp --dport 80  -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# SSH 仅允许指定来源
iptables -A INPUT -p tcp -s 203.0.113.5 --dport 22 -j ACCEPT
# 允许 ICMP 便于排错(可选)
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT

# 持久化保存
iptables-save > /etc/iptables/rules.v4      # Debian: apt install iptables-persistent
# service iptables save                     # RHEL/CentOS 传统方式

第三步:从外部验证——自证的收敛不算数

在服务器里看全是安全的,不代表外部真的访问不到。必须用外部主机交叉验证。

bash
# 在另一台机器(或用手机热点笔记本)上执行
# 快速扫描常见 1000 个端口
nmap -Pn -sV 203.0.113.10

# 全端口扫描(慢,但最彻底)
nmap -Pn -p- 203.0.113.10

# 只验证某几个端口是否可达
nmap -Pn -p 22,3306,6379,27017 203.0.113.10

# 无 nmap 时用 TCP 握手判断(返回 Connected 即为开放)
timeout 3 bash -c 'cat < /dev/null > /dev/tcp/203.0.113.10/3306' && echo "3306 开放" || echo "3306 不可达"

同时必须检查云厂商安全组——它是在宿主机虚拟化层生效的,优先级高于系统内部的 iptables。常见的坑是:系统里明明用 firewalld 封了 3306,但安全组里写了 0.0.0.0/0 允许 3306,端口照样对外可见。

bash
# 用在线端口扫描服务交叉验证(第三方视角最客观)
# 或直接看当前防火墙与安全组的差异
iptables -L INPUT -n -v --line-numbers
firewall-cmd --list-all-zones | head -40

常见误区 / 排错提示

  1. 只在系统防火墙里封端口,忘了云安全组。 云主机的安全组在最外层生效,是真正的第一道关卡;请先在安全组收敛,再用系统防火墙做二次防护。
  2. 把 MySQL 改成 3307 就以为安全了。 改端口阻挡不了 nmap -p- 全端口扫描;真正有效的是绑定内网地址 + 来源 IP 白名单 + 强口令。
  3. 直接关掉 SSH 端口导致失联。 调整 SSH 相关规则前,务必保留已建立会话并确认存在 VNC/控制台备用通道。
  4. iptables 规则没保存,重启后全部丢失。 规则写完后必须执行 iptables-save(Debian 装 iptables-persistent,RHEL 装 iptables-services),否则一次重启全部白配。
  5. ss -lntup 看不到端口就以为关闭了。 UDP 服务必须加 -u 参数才会显示;同时要排除 ICMP 与 IPv6(-4/-6)的盲区。
  6. Docker 容器端口绕过系统防火墙。 Docker 直接操作 iptables 并把映射端口暴露到所有网卡,若只需本机访问应写成 -p 127.0.0.1:8080:8080。

常见问题(FAQ)

服务器上开放多少个端口才算合理?

对外(公网)通常只需要 2~3 个:80、443,以及仅在必要时开放的 SSH 管理端口。对内可以按需开放数据库、缓存、RPC 端口。判断标准是"数得过来且每条都能说清用途",一台服务器对外暴露超过 5 个端口就应该重新审视架构是否合理。

Redis 6379 端口被扫描到有什么后果?

后果非常严重。未设置密码的 Redis 可以被攻击者直接写入 ~/.ssh/authorized_keys 从而获得服务器登录权限(俗称 Redis 未授权访问 Getshell),近年来大量"删库跑路"和挖矿事件都源于此。必须绑定内网或 127.0.0.1、设置 requirepass、并用 rename-command 禁用高危命令。

关闭 iptables 或 firewalld 能提升性能吗?

基本没有可感知的提升,却会带来巨大风险。现代内核的 netfilter 处理性能开销极小,所谓"防火墙影响性能"通常是 NAT 连接追踪表(conntrack)被打满导致的,正确做法是调大 nf_conntrack_max 而不是关闭防火墙。

为什么本地 telnet 能通,但外部访问不了?

最常见原因是多层防护叠加:ss -lntp 显示 127.0.0.1:端口 表示只监听回环,外部自然访问不到;其次依次检查 firewalld/iptables 规则、云厂商安全组、运营商或机房的上游 ACL。按"本机 → 系统防火墙 → 安全组 → 上游网络"四层逐级排查即可定位。

端口扫描自己的服务器违法吗?

扫描自己拥有或管理的服务器完全合法,是正常的安全自查手段。但对不属于自己的服务器发起端口扫描在多数司法辖区属于违法或违反服务商条款的行为,可能触发对方的告警甚至法律追责。请只在授权范围内操作。

Docker 部署的服务端口应该怎么收敛?

关键是端口映射写法。只希望本机访问时写成 docker run -p 127.0.0.1:8080:80 nginx;需要外网访问但不想被 Docker 绕过 iptables 时,可在 /etc/docker/daemon.json 中设置 "iptables": false 并自行管理规则,或使用 --network=host 之外的桥接配置,并统一在宿主机防火墙做控制。