批量建站最怕的不是单点被黑,而是同一个漏洞在所有站点上被批量利用。一套程序部署几十上百个站,只要一个站的防护逻辑被绕过,攻击者就能用相同手法横向打穿整批服务器。所以批量建站的防护重点不是“每个站都做一遍安全设置”,而是把漏洞收敛到统一入口,让防护动作只做一次、却能覆盖全部站点。

先说最容易被忽略的目录隔离问题。很多批量建站环境里,不同站点共用同一个运行用户,站点目录权限默认755或775,导致A站被上传木马后可以直接读写B站目录。正确的做法是给每个站点分配独立的运行用户,或者至少用PHP-FPM的独立进程池隔离。Nginx或Apache配置里,每个站点对应不同的php-fpm pool,每个pool用不同的user和group,这样即使单站沦陷,攻击者也拿不到跨站读写权限。权限隔离是批量防护的地基,没有这个前提,后面所有策略都白搭。

统一入口防护的第二层是Web应用防火墙规则。批量建站的程序通常来自同一套CMS或自研框架,漏洞特征高度一致。与其在每个站点装安全插件,不如在Nginx或OpenResty层做统一的WAF规则过滤。把常见的SQL注入、XSS、文件包含、目录遍历攻击特征写成正则规则,在server块或者http块统一加载。这样不管后面增加多少个站点,只要域名解析过来,流量都会先经过这层规则过滤。

# 统一WAF规则示例(Nginx http块)
http {
    # 拦截常见SQL注入特征
    map $request_uri $sql_inject {
        default 0;
        "~*(\%27)|(\')|(\-\-)|(\%23)|(#)" 1;
        "~*((select|union|insert|update|delete).*(\%27|\s))" 1;
    }
    # 拦截常见XSS特征
    map $request_uri $xss_attack {
        default 0;
        "~*((\%3C)|<)([^>]*)(script|iframe|onerror|onload)" 1;
    }
    # 拦截路径穿越
    map $request_uri $path_traversal {
        default 0;
        "~*(\.\./|\.\.\%2f|\.\.\%5c)" 1;
    }
}

上面这段规则只是基础示例,实际部署时还需要根据自己程序的特征做白名单放行,避免误杀正常请求。关键点在于:规则放在http块里,所有server自动继承,不需要每个站点单独配置。如果用的是OpenResty,还可以用lua-resty-waf这类模块做更精细的规则管理,支持热更新规则库,不用重启服务。

统一漏洞修复的核心在于代码分发机制。批量建站如果每个站都单独维护一份代码,补丁根本打不过来。必须建立统一的代码仓库和发布流程,所有站点从同一个代码分支拉取更新。发现漏洞后,在代码仓库修一次,然后通过自动化脚本批量同步到所有站点。这里要注意,同步动作不能只覆盖核心文件,必须包含配置文件、模板文件、上传目录的默认文件。很多攻击者会利用旧版本遗留的install目录、备份文件、测试文件进行攻击,所以同步时要做到全量覆盖,而不是增量更新。

文件上传目录是批量建站的重灾区。攻击者一旦找到上传漏洞,第一件事就是往上传目录写webshell。统一防护的做法是在Web服务器层直接禁止上传目录执行任何脚本。Nginx配置里对uploads、attachments、static这类目录单独设置location,只允许静态文件访问,禁止PHP解析。

# 禁止上传目录执行PHP
location ~* ^/(uploads|attachments|static|data)/.*\.(php|php5|php7|phtml|pht)$ {
    deny all;
    return 403;
}

# 或者更彻底:上传目录完全不经过PHP解析
location ^~ /uploads/ {
    location ~ \.php$ {
        deny all;
    }
    try_files $uri $uri/ =404;
}

这个配置同样放在http块或者通过include统一引入,所有站点自动生效。如果程序有特殊的上传目录名称,需要在配置里把所有可能的目录都列进去。另外还要注意,攻击者可能会把webshell伪装成.jpg、.png等图片后缀,但内容里包含PHP代码。所以除了禁止PHP后缀解析,还要检查上传目录里的文件是否包含可执行代码特征。可以用脚本定期扫描上传目录,发现包含<?php、eval、base64_decode等特征的文件直接隔离。

批量建站的数据库防护也要统一。很多站长为了省事,所有站点共用一个数据库账号,而且权限是ALL PRIVILEGES。一旦单站被SQL注入打穿,攻击者能直接跨库读取所有站点的数据。正确做法是每个站点使用独立的数据库账号,只授予对应数据库的权限。MySQL或MariaDB里可以这样批量创建:

-- 为每个站点创建独立账号(示例)
CREATE USER 'site_001'@'localhost' IDENTIFIED BY '随机强密码';
GRANT SELECT, INSERT, UPDATE, DELETE ON site_001_db.* TO 'site_001'@'localhost';
-- 禁止该账号访问其他数据库
REVOKE ALL PRIVILEGES ON *.* FROM 'site_001'@'localhost';
FLUSH PRIVILEGES;

如果站点数量很多,可以用脚本批量生成账号和授权语句。数据库连接信息不要硬编码在程序里,统一放在配置文件里,并且配置文件放在Web根目录之外。批量建站时,配置文件路径要规范化,比如统一放在/data/config/站点名/下面,Web服务器无法直接访问到这个路径。

登录入口的统一防护经常被忽视。批量建站的后台地址如果都是默认的/admin、/manage、/wp-admin这类路径,攻击者用扫描器批量爆破的成功率会大幅提高。统一做法是修改默认后台路径,每个站点使用不同的随机路径,同时在Web服务器层对后台路径做访问限制。可以设置只允许特定IP访问后台,或者加上HTTP Basic Auth二次验证。

# 后台路径统一加访问限制
location ~ ^/(admin_随机字符串|manage_随机字符串) {
    allow 你的办公IP;
    deny all;
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;
    # 继续转发给PHP处理
    try_files $uri $uri/ /index.php?$query_string;
}

后台路径的随机化不能只改目录名,还要在程序内部做一致性校验。很多CMS在配置文件里定义了后台入口常量,改目录名只是表面功夫,攻击者通过源码泄露或报错信息还是能拿到真实路径。所以统一防护要做到:后台路径随机化+访问来源限制+登录失败次数限制+验证码或二次验证。登录失败次数限制可以在Nginx层用limit_req模块实现,也可以在程序层做,但程序层容易被绕过,建议两层都做。

批量建站的日志监控必须集中化。每个站点单独看日志效率太低,攻击者打A站失败后会立刻转向B站,如果日志分散在不同服务器上,很难发现这种横向尝试。统一做法是把所有站点的访问日志和错误日志集中到一个日志收集系统里,比如用rsyslog或filebeat把日志实时推送到中心服务器。然后在中心服务器上做规则匹配,发现同一IP在短时间内访问多个站点的可疑路径,或者尝试多个站点的后台登录,立即触发告警。

# 集中日志收集示例(rsyslog客户端配置)
# 在每台Web服务器上配置
module(load="imfile")
input(type="imfile" File="/var/log/nginx/access.log" Tag="nginx-access" StateFile="nginx-access-state")
input(type="imfile" File="/var/log/nginx/error.log" Tag="nginx-error" StateFile="nginx-error-state")
# 发送到中心日志服务器
*.* @中心日志服务器IP:514

日志集中后,可以写脚本定期分析,比如统计每个IP访问的站点数量、访问的后台路径、触发的WAF规则次数。如果某个IP在5分钟内访问了超过10个站点的后台路径,或者触发了多次SQL注入规则,直接加入封禁列表。封禁动作也要统一,在Nginx层用共享的黑名单文件,所有站点自动引用。

还有一个容易被批量建站忽略的点:站点程序版本信息泄露。很多CMS会在响应头、页面底部、报错信息里暴露版本号。攻击者拿到版本号后,直接对照公开漏洞库批量扫描。统一防护要在Web服务器层隐藏这些信息,比如在Nginx里关闭版本号显示,在程序层统一处理报错页面,不输出详细的错误堆栈。对于响应头里的X-Powered-By、Server这类字段,统一在http块里用more_set_headers模块清除。

# 隐藏服务器版本信息
http {
    server_tokens off;
    more_clear_headers "X-Powered-By";
    more_clear_headers "Server";
    # 自定义错误页面,避免泄露路径信息
    error_page 500 502 503 504 /error.html;
    location = /error.html {
        root /data/error_pages;
        internal;
    }
}

批量建站的定时任务和计划脚本也要纳入统一管理。很多站点程序依赖cron做定时清理、数据同步、备份等操作。如果这些脚本有漏洞,攻击者可以通过Web端触发或者利用脚本的权限问题提权。统一做法是把所有站点的cron任务集中到一个管理脚本里,脚本本身放在Web目录之外,只允许root或特定系统用户执行。脚本内部对传入参数做严格校验,不接受任何来自Web端的直接输入。

备份文件的统一管理同样关键。批量建站如果开启自动备份,备份文件经常会被放在站点目录下的backup文件夹里,或者放在可预测的路径下。攻击者一旦猜中备份文件路径,直接下载整个站点源码和数据库。统一做法是把备份文件统一存放到Web根目录之外的专用备份目录,并且备份文件命名加上随机字符串和时间戳。如果必须放在Web目录下,要在Nginx层禁止访问备份文件后缀。

# 禁止访问备份文件
location ~* \.(sql|tar|gz|zip|bak|old|swp|swo)$ {
    deny all;
    return 404;
}

批量建站的SSL证书管理也影响安全。如果所有站点共用一个泛域名证书,证书泄露后所有站点都受影响。建议每个站点使用独立的证书,或者至少按业务分组使用不同的证书。证书文件统一放在一个安全目录,权限设置为600,只允许Web服务器读取。证书续期要自动化,避免过期后站点被浏览器标记为不安全,用户数据在传输中被劫持。

最后说一个实操层面的建议:批量建站的防护策略要写成文档和脚本,而不是靠人工记忆。每增加一个新站点,自动执行一遍安全基线检查脚本,检查目录权限、数据库账号、后台路径、WAF规则、日志配置是否都符合统一标准。这个脚本可以做成简单的shell或python脚本,在新站上线前跑一遍,输出不符合项清单。只有把防护动作标准化、自动化,批量建站才不会随着站点数量增加而失控。

批量建站的统一漏洞防护,本质上是用工程化的思路解决规模化带来的安全边际递减问题。单站安全靠人工盯可以,几十上百个站必须靠统一配置、统一规则、统一监控、统一响应。把防护点从“每个站”上移到“入口层”和“管理层”,才能用有限的精力守住整批站点的安全底线。