多数开发者很在意代码写得安不安全。参数化查询、输入校验、依赖扫描,一整套都做。习惯是好习惯。
然后他们把这份干净的代码,部署到一台 22 端口对全世界敞开、root 可以登录、应用用 root 跑、前面没有任何限流的服务器上。代码再干净,服务器配置错了,门一样是开的。难的那部分白做了。
下面是上线前该锁好的 5 项服务器加固基础。没有一项冷门。全部都能挡掉新 VPS 拿到 IP 后几分钟内就涌上来的那批自动化流量。
不是开发者?看这一段就好。
后面很快就进入技术细节。如果你是老板但不写代码,你不需要看懂那些命令,你只需要知道上线前该问开发团队什么。5 个问题:
- “服务器上的密码登录关掉了吗,改成 SSH key 了吗?”(是 / 否)
- “有防火墙吗?除了应用真正会用的端口,其他都关了吗?”(是 / 否)
- “装了 Fail2Ban 或类似的东西,去挡反复登录失败的尝试吗?”(是 / 否)
- “我们的应用是用普通用户跑的,不是 root 吧?”(是 / 否)
- “应用前面有 Nginx 或 Caddy 这类反向代理,负责 HTTPS 吗?”(是 / 否)
任何一题答“没有”或“不确定”,先别上线。这些事对懂的人来说,每一项都花不到 30 分钟。帮你架服务器的人,应该能白纸黑字确认这 5 项都做了。
1. 禁用 root 登录,改用 SSH key
一台全新 Linux VPS 的默认状态,就是最糟的状态。22 端口开着,root 可以登录,密码认证也开着。开通后几个小时内,你就会看到成千上万次登录失败记录,全是机器人在试弱的 root 密码。
两个改动就能关上这道门。
先建一个有 sudo 权限的普通用户,然后换成 SSH key 认证。在自己的电脑上用 ssh-keygen -t ed25519 生成 key,用 ssh-copy-id user@yourserver 把公钥传上服务器,登录一次确认可用,再到 /etc/ssh/sshd_config 里关掉密码登录和 root 登录:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
用 sudo systemctl restart sshd 重启 SSH,并且在另一个终端确认 key 能登进去之前,原来那个会话千万别关。把自己锁在外面,你就得在凌晨两点付钱给 VPS 供应商开 console。
SSH key 不是“比较难破”。它是在任何合理时间内都不可能被暴力破解的。密码是猜得中的。这两者没得比。
2. 装好防火墙
每一个开着的端口背后都是一个服务。每个服务都有版本。每个版本都有已公开的已知漏洞。防火墙让你一句话说清“只开这几个端口,其他免谈”,不用再争。
在 Ubuntu 上,UFW 三条命令就能做掉 90%:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
就这样。5432 上的 Postgres、6379 上的 Redis、跑在 3000 或 8080 的应用服务器?外网一个都碰不到。你的 web 应用通过 localhost 跟它们讲话,这正是你要的。
防火墙就是一条锁上门的走廊。只开应用真的要走的那几扇。
我们做代码审查时最常看到的错误:.env 里写着 DB_HOST=0.0.0.0,Postgres “为了开发方便”监听所有网卡。这种配置滑进生产环境时,防火墙救的是你自己。
如果你的 VPS 供应商有网络层防火墙(DigitalOcean Cloud Firewalls、AWS Security Groups、Hetzner Cloud Firewall),两层都用。纵深防御是真有用,不是做样子。
3. 装 Fail2Ban
Fail2Ban 盯着你的日志文件。当某个 IP 的 SSH 登录、web 应用登录失败,或者命中你配置的任何规则,Fail2Ban 就临时加一条 iptables 规则丢掉那个 IP 的流量。默认是 10 分钟内失败 3 次,封 10 分钟。
装好并启用:
sudo apt install fail2ban
sudo systemctl enable --now fail2ban
默认配置已经保护 SSH。要给 Nginx 认证、WordPress 登录,或你自家应用的登录失败端点加自定义 jail,也就几行配置。
有两点要知道。第一,Fail2Ban 不能取代防火墙。防火墙决定什么碰得到,Fail2Ban 决定谁已经不受欢迎,两者是分层的。第二,如果你的应用在 Cloudflare 或负载均衡器后面,每个请求看起来都来自同一个代理 IP,除非你配置它从 X-Forwarded-For 读真实 IP,否则 Fail2Ban 形同虚设。先确认,再去相信那些封禁记录。
4. 永远不要用 root 跑应用
如果你的 Node、Python 或 Laravel 应用是用 sudo 启动的,理由是“这样才跑得起来”,请停手。你等于把 root 权限交给了每一个依赖、每一个 NPM 包、每一个 Composer 库。只要一次供应链被攻破(今年早些时候的 LiteLLM 事件就是新鲜例子),攻击者就拿下整台机器。
给应用建一个专用的、无特权的用户:
sudo adduser --system --group --no-create-home appuser
sudo chown -R appuser:appuser /var/www/myapp
然后用这个用户跑应用。systemd unit 文件写起来很简单:
[Service]
User=appuser
Group=appuser
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/node server.js
万一 appuser 被拿下,攻击者拿到的是这个应用的文件和数据库凭证。这已经很糟。但他装不了 rootkit、改不了 SSH 配置、读不了其他用户的文件,也没法横向跳到机器上的其他服务。影响范围被圈在一个应用里。
Docker 上同理。Dockerfile 里写 USER node 或 USER 1000,别用默认的 root。容器本身默认不是安全边界,把它当成需要用无特权用户来跑的进程就对了。
5. 在应用前面放一个反向代理
你那个跑在 3000 端口的 Node 应用,单线程送 HTTP 送得挺开心。然后 10 个机器人发现了它,拿畸形请求猛砸,event loop 就没了。反向代理(Nginx、Caddy、Traefik)的做法是站在外网和你的应用之间,把脏活接下来。
实际拿到什么:
- TLS termination。 Caddy 几行配置就给你 HTTPS 和自动续期的 Let's Encrypt 证书。Nginx 配 Certbot 也就 5 分钟。别再让 Node 进程去处理 SSL。
- 响应头加固。
Strict-Transport-Security、X-Frame-Options、Content-Security-Policy,全在代理层加上,应用一行都不用改。 - 限流。 Nginx 的
limit_req能限制单个 IP 打你/login端点的频率。搭配 Fail2Ban,多数撞库攻击在碰到应用之前就死了。 - 连接缓冲。 慢速客户端(
Slowloris那类攻击)耗的是代理的连接,不是你应用的。Nginx 吸收这类请求比 Node 或 Express 强得多,尤其是把client_header_timeout、client_body_timeout和limit_conn设成合理值之后。Nginx 本身不是万灵丹,但它把故障形态从“应用挂了”变成“几条代理连接被占着”。
小团队用 Caddy 最省事。Caddyfile 写几行,HTTPS、HTTP/2 和一套合理默认值就有了。要精细控制的话,Nginx 是无聊但久经考验的选择。挑一个用。别让应用直接跑在 80 或 443 端口上。
这些还是保护不了你的部分
这 5 件事挡的是机器人和懒人。挡不了的是:
- 你代码里的 SQL injection(写参数化查询)。
- 公开 GitHub repo 里泄露的
.env(审一遍你的提交记录)。 - 开发者的电脑被入侵、SSH key 就在上面(给 key 设密码短语,定期轮换)。
- 依赖里的供应链攻击(锁版本,盯 CVE 通报)。
- 手上有你这套技术栈零日漏洞的、有心的攻击者。
加固是底线。应用安全是另一个问题,它住在你的代码、CI pipeline 和依赖管理里。我们之前写过 vibe coding 的安全风险和 AI 工具的供应链攻击。想看应用层那一面,可以读这两篇。
PDPA 这一层
如果你的马来西亚公司在这台服务器上存了任何个人资料(姓名、IC 号码、电话、电邮,任何能识别到人的东西),PDPA 你想不想面对都得面对。2024 年修正案对达到门槛的事件设了 72 小时通报窗口,而“我们把 root SSH 开着”这种理由,监管方不会觉得情有可原。
基础加固不是合规的万灵丹。但因为省掉它而出事,是最快触发通报义务、连带赔上名声的路径之一。把这 30 分钟当成你买过最便宜的保险。
我们的看法
马来西亚中小企业和初创的部署,我们审过不少。模式很一致:应用代码干净,服务器配置薄弱。开发者被教过怎么安全地写 SQL,很少被教过怎么安全地部署。早期公司出的安全事故,大多不是从什么精巧的漏洞利用开始。它们的开头是:22 端口对全世界开着,密码登录没关,攻击者用 root:admin123 进来,然后是 6 个星期没人发现的缓慢横向移动。
解法不好看,但是快。懂的人 30 分钟就能把一台新 VPS 弄到可上线的加固状态。第一次做,给自己一小时和一份清单。
这 5 件事不会让你的服务器刀枪不入。它们会让攻击者转头去找更好下手的目标。老实说,这就是目标。
上线前想找人帮你的生产环境看一眼?我们的网络安全团队经常做这类检查。WhatsApp 找我们或留个言,我们陪你把配置过一遍。不推销。




