云服务器不像虚拟主机那样有服务商帮你做一层基础隔离,从你按下购买按钮的那一刻起,安全责任几乎完全落在你自己身上。很多人以为装个宝塔面板、改个SSH端口就万事大吉,实际上服务器每天遭受的自动化扫描和暴力破解远超你的想象。下面直接进入具体的安全防护操作,不讲空泛的概念。
操作系统的初始防线:别让大门敞开着拿到一台新装的云服务器,第一件事不是装Nginx,而是立刻加固SSH访问。默认的22端口是全网扫描器重点关照的对象,你的root账户和密码组合可能在你还没登录之前就已经被尝试了上千次。修改SSH默认端口是最基础的,但仅仅改端口不够。编辑SSH配置文件 /etc/ssh/sshd_config,将 PermitRootLogin 设置为 no,这意味着你必须先用普通用户登录,再通过 su 或 sudo 提权。攻击者连root用户名都猜不到,暴力破解的成功率直接归零。
密钥认证比密码可靠得多。在本地生成一对密钥,把公钥上传到服务器,然后关闭密码登录,将 PasswordAuthentication 设为 no。这样一来,没有对应私钥的机器根本无法建立SSH连接。如果你的团队有多个人需要登录,每人生成自己的密钥对,公钥统一追加到 authorized_keys 文件中,权限严格设为600。有人离职时,只需从文件里删掉他那行公钥即可,不用修改任何共享密码。
还有一个容易被忽略的细节:SSH的版本协议。确保配置文件中有 Protocol 2 这一行,SSHv1存在严重的设计缺陷,早已被淘汰。修改完所有配置后重启sshd服务,不要断开当前连接,另开一个终端窗口测试能否正常登录,确认无误后再关闭旧会话。这个习惯能避免你把自己锁在服务器外面。
系统层面的访问控制:把权限关进笼子操作系统防火墙是第二道闸门。云服务商提供的安全组规则作用于网络层,而系统防火墙如iptables或firewalld作用于主机层,两者叠加使用才够稳妥。默认策略应该是拒绝所有入站流量,只放行你明确需要的端口。比如你的Web应用只需要80和443端口对外,那就只开放这两个,SSH的管理端口仅对你的办公IP放行。如果你没有固定IP,至少设置一个IP白名单范围,或者通过跳板机来访问。
这里给出一段iptables的基础规则示例,你可以根据自己的端口和IP调整:
# 清空现有规则 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 state --state ESTABLISHED,RELATED -j ACCEPT # 允许特定IP访问SSH管理端口,假设改为2222 iptables -A INPUT -p tcp -s 你的办公IP --dport 2222 -j ACCEPT # 允许Web端口 iptables -A INPUT -p tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -j ACCEPT # 保存规则 iptables-save > /etc/iptables.rules
用户权限管理同样关键。永远不要用root身份运行你的Web应用或数据库。为每个服务创建独立的系统用户,例如 www-user 跑Nginx,mysql-user 跑MySQL,这些用户的家目录设为不存在的路径,shell设为 /sbin/nologin,它们只是服务的载体,不具备登录能力。网站文件的所有者应该是专门的部署用户,而非运行Web服务的用户,这样即使Web进程被攻破,攻击者也无法直接修改网站源码。
Web应用层的深度防御:请求在到达代码之前先被过滤Nginx或Apache直接暴露在公网上,它们的配置直接决定了大量攻击能否生效。首先,隐藏你的Web服务器版本信息。在Nginx中,http块里设置 server_tokens off,这样错误页面和响应头里就不会出现具体的版本号。攻击者扫描时无法快速判断你的软件版本,也就难以针对已知漏洞发动精准攻击。
限制请求速率是抵御CC攻击和暴力破解的有效手段。在Nginx中,利用 limit_req_zone 和 limit_req 指令可以精确控制单个IP的访问频率。以下配置定义了一个名为 req_limit 的共享内存区域,限制每个IP每秒最多10个请求,突发不超过20个:
http {
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
server {
location / {
limit_req zone=req_limit burst=20 nodelay;
}
# 对登录接口更严格的限制
location /login {
limit_req zone=req_limit burst=3 nodelay;
}
}
}
对于上传功能,必须限制请求体大小。在Nginx中设置 client_max_body_size 10m,在PHP的php.ini中同步设置 upload_max_filesize 和 post_max_size。不要让用户上传的文件直接存储在Web可访问的目录下,而是放在非Web根目录,通过专门的脚本来读取和输出。上传文件的扩展名要做白名单校验,不能依赖黑名单,因为攻击者总能找到你没想到的扩展名组合。
HTTPS的配置远不止申请一张证书那么简单。启用HSTS头,告诉浏览器在指定时间内只能通过HTTPS访问你的域名,杜绝SSL剥离攻击。在Nginx中添加一行 add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always。同时禁用过时的SSL协议和弱加密套件,只开启TLS 1.2和1.3,加密套件选择支持前向加密的组合。
Web应用防火墙的实战部署:用规则说话ModSecurity是一款开源的Web应用防火墙,作为Nginx或Apache的模块运行,能在请求到达你的应用代码之前就进行拦截。它自带OWASP核心规则集,覆盖了SQL注入、跨站脚本、文件包含等常见攻击模式。安装ModSecurity后,默认的规则集可能会产生误报,你需要根据自己应用的实际情况进行调优。初期可以先运行在检测模式,只记录日志不拦截,分析一段时间后再切换到防护模式。
ModSecurity的规则配置相对复杂,如果你需要更轻量的方案,可以考虑Nginx的lua模块配合自定义脚本实现简单的WAF逻辑。比如检测请求参数中是否包含常见的SQL注入关键词,或者User-Agent是否为空或包含扫描器特征。这些脚本不需要太复杂,几十行代码就能过滤掉大部分自动化攻击。
对于PHP应用,务必禁用危险函数。在php.ini中找到 disable_functions 配置项,把 exec、system、passthru、shell_exec、popen、proc_open 这些能执行系统命令的函数全部加入禁用列表。同时设置 allow_url_fopen 和 allow_url_include 为 Off,防止远程文件包含漏洞被利用。这些配置不会影响绝大多数正常业务,却能堵死一大类攻击手法。
数据库的安全加固:数据是最后一道底线数据库默认监听在127.0.0.1上,绝对不要绑定到0.0.0.0。如果你的应用和数据库在同一台服务器上,通过Unix Socket连接比TCP连接更快更安全。MySQL的root密码设置后,删除匿名用户和测试数据库,这是安装完成后必须立刻执行的操作。为每个应用创建独立的数据库用户,并且只赋予该应用所需的最小权限。一个只读的展示页面,它的数据库用户就不应该拥有INSERT或DELETE权限。
数据库的备份策略是安全体系的重要一环。每天自动备份并加密存储到异地,云服务商的对象存储服务通常成本很低。备份文件不要直接放在服务器本地磁盘上,因为一旦服务器被入侵,攻击者可以轻易删除所有备份。设置备份文件的保留周期,至少保留最近7天的每日备份和最近3个月的每周备份。定期做恢复演练,确保备份文件真的可用,而不是到灾难发生时才发现备份早已损坏。
文件完整性与日志监控:知道发生了什么网站文件被篡改是入侵的典型迹象。部署文件完整性监控工具,如AIDE或Tripwire,它们会为所有关键文件生成哈希值快照,之后定期比对,一旦发现变化就发出告警。监控的范围至少包括Web目录、系统二进制文件目录和配置文件目录。更新程序或系统补丁后,记得重新生成基线快照,否则每次正常更新都会触发大量误报。
日志的价值怎么强调都不过分。确保Nginx的访问日志和错误日志、系统认证日志、数据库日志都在正常记录。集中式日志管理能让分析变得容易,你可以用rsyslog把多台服务器的日志汇总到一台安全的日志服务器上,或者使用开源的ELK套件进行可视化和检索。关键日志要实时监控,设置告警规则,比如一分钟内同一IP出现50次401认证失败,就自动触发告警通知你。
日志文件本身也要防止被篡改或删除。将日志目录的权限设置为仅root和日志进程可写,普通用户只能读取。如果条件允许,把日志实时同步到不可变的存储中,这样即使攻击者拿到了root权限,也无法抹掉入侵痕迹。
内核参数与网络层的额外加固Linux内核参数通过sysctl可以调整网络行为,增强抗攻击能力。编辑 /etc/sysctl.conf 文件,开启SYN Cookie保护,防止SYN洪水攻击:net.ipv4.tcp_syncookies = 1。忽略ICMP重定向消息,防止路由被劫持:net.ipv4.conf.all.accept_redirects = 0。禁用IP源路由,防止攻击者指定数据包的传输路径:net.ipv4.conf.all.accept_source_route = 0。开启反向路径过滤,防止IP地址欺骗:net.ipv4.conf.all.rp_filter = 1。这些参数修改后执行 sysctl -p 使其立即生效。
如果你的网站不需要IPv6,在系统层面直接禁用它,减少攻击面。在sysctl中设置 net.ipv6.conf.all.disable_ipv6 = 1,同时修改SSH配置确保sshd不监听IPv6地址。很多管理员只关注IPv4的防护,却忽略了IPv6上同样存在扫描和攻击。
容器化环境的特殊考量如果你使用Docker部署应用,容器的安全配置不能忽视。不要以root用户运行容器内的进程,在Dockerfile中使用USER指令切换到非特权用户。挂载宿主机目录时使用只读模式,除非容器确实需要写入。限制容器的系统调用能力,通过 --cap-drop 去掉不必要的内核能力,通过 --security-opt no-new-privileges 禁止容器内的进程获取额外权限。定期扫描镜像中的已知漏洞,基础镜像尽量选用官方的精简版本,减少攻击面。
容器之间的网络隔离也很重要。不要把不相关的容器放在同一个Docker网络中,为每个应用栈创建独立的网络。如果某些容器不需要访问外网,通过内部网络连接并禁用其出站访问。宿主机的Docker守护进程本身也需要加固,不要将Docker的Unix Socket暴露给不信任的用户,因为能访问这个Socket就等于拥有了root权限。
持续维护:安全不是一次性工作所有软件都在不断被发现漏洞,你的服务器上运行的操作系统、Web服务器、数据库、编程语言运行时、第三方库,每一个组件都需要持续关注。配置自动化的安全更新,至少对操作系统的基础包启用无人值守升级。对于应用依赖的第三方库,使用依赖扫描工具定期检查,发现高危漏洞后评估影响并尽快修复。
定期做一次完整的安全审计,检查开放端口是否都是必要的,检查系统用户列表是否有可疑账户,检查计划任务里是否有异常条目,检查Web目录下是否有近期被修改过的文件。这些检查可以写成脚本定时运行,结果发送到你的邮箱。人不可能每天手动做这些事情,但脚本可以。
最后,永远做好最坏的打算。假设服务器某天被完全攻破,你的应急响应计划是什么?哪些服务需要优先恢复?域名解析能否快速切换到备用服务器?数据库的最后一份干净备份在哪里?这些问题在平静时期想清楚,比在危机中手忙脚乱要强得多。
