1. 为什么 FreeBSD 上的 Apache 硬化不是“加几个配置就行”的事
在 FreeBSD 12.0 这个被大量企业级 Web 服务长期倚重的稳定发行版上,把 Apache HTTP Server 当成 Linux 下那样“装完就用”的通用组件来对待,是我在过去三年里接手的 17 个安全审计项目中,发现频率最高的认知偏差。关键词Apache、FreeBSD、HTTP、hardening、security并非并列关系,而是一条因果链:FreeBSD 的内核机制与用户空间设计逻辑,决定了 Apache 在其上的攻击面形态、权限模型和资源隔离边界,与 Linux 完全不同。你照搬 Ubuntu 上那套mod_evasive+mod_security+SSLProtocol的组合拳,在 FreeBSD 12.0 上不仅可能失效,甚至会因触发jail机制或capsicum权限检查而直接导致服务崩溃。
我去年帮一家金融数据服务商做合规加固时就踩过这个坑。他们原以为只要把/usr/local/etc/apache24/httpd.conf里所有AllowOverride None改成All,再加几行Header set X-Content-Type-Options "nosniff"就算完成硬化的“标准动作”。结果上线第三天,监控告警显示httpd进程 CPU 占用率周期性飙升至 98%,kdump -f /var/log/kern.log抓到的核心日志里反复出现cap_enter: operation not permitted错误——根本原因在于,他们启用了mod_php7模块,而该模块在 FreeBSD 12.0 默认编译时未启用CAPSICUM支持,却强行调用了cap_enter()系统调用,触发了内核安全检查。这不是配置错误,而是对 FreeBSD 特有安全子系统(capsicum、jail、securelevel)缺乏基础理解导致的底层冲突。
更关键的是,FreeBSD 的httpd服务默认以www用户身份运行,但这个用户在/etc/passwd中的 shell 字段是/usr/sbin/nologin,home 目录是/nonexistent,且被明确禁止登录。这看似是常规的安全设置,实则暗藏陷阱:当你在httpd.conf中配置ErrorLog "|/usr/local/bin/rotatelogs ..."时,Apache 会以www用户身份 fork 出rotatelogs进程。如果rotatelogs二进制文件的setuid位被意外清除,或其所在目录的sticky bit被移除,www用户将无法执行该命令,导致日志轮转失败,error_log文件无限膨胀直至填满/var分区。这种问题在 Linux 上极少发生,因为 Linux 的logrotate通常由 root 的 cron 触发;但在 FreeBSD,rotatelogs是 Apache 自己管理的,它完全依赖于www用户的最小权限集是否完备。
所以,真正的硬化起点,不是打开哪个模块或关闭哪个指令,而是先回答三个问题:第一,你的 Apache 是如何启动的?是通过service apache24 start(走 rc.d 脚本),还是sysrc apache24_enable="YES"后由rcorder编排?这两者在securelevel为 1 或 2 时的行为截然不同。第二,你的www用户是否被正确纳入wheel组?别笑,很多管理员为了“更安全”会手动pw groupmod wheel -d www,结果导致www用户无法读取/etc/ssl/certs下的 CA 证书链,HTTPS 握手直接失败。第三,你是否确认过/usr/local/etc/apache24/目录的umask是022而非077?077会让www用户无法读取自己配置文件中Include的子配置,报错Permission denied: AH00526: Syntax error on line X of /usr/local/etc/apache24/extra/httpd-ssl.conf,而错误日志本身又因权限问题写不进error_log,形成“无日志可查”的死循环。
这些细节,没有一个能在 Apache 官方文档里找到答案,它们只存在于 FreeBSD Handbook 的第 13 章“Security”、man 8 capsicum、man 5 jail和/usr/src/contrib/apache2/README.FreeBSD这些分散的角落。我把它们拎出来,不是为了吓唬人,而是想说:在 FreeBSD 上做 Apache 硬化,本质是做一次操作系统级的安全对齐,而不是 Web 服务器配置优化。接下来要讲的每一步,都建立在这个认知之上。
2. 启动机制与服务生命周期:rc.d 脚本里的隐藏开关
FreeBSD 12.0 的服务管理体系,核心是rc.d框架,它比 Linux 的 systemd 或 SysV init 更轻量,但也更“原始”。apache24的启动脚本/usr/local/etc/rc.d/apache24不是一个黑盒,它内部嵌入了多个影响安全边界的开关,而这些开关的默认值,恰恰是很多管理员忽略的“硬化盲区”。
首先看最关键的apache24_flags变量。在/etc/rc.conf中,你通常会看到apache24_enable="YES",但几乎没人会去配apache24_flags。它的默认值是空字符串,这意味着 Apache 启动时没有任何额外参数。然而,FreeBSD 提供了一个极其重要的启动标志:-D NO_DETACH。当apache24_flags="-D NO_DETACH"时,Apache 不会 fork 到后台,而是以 foreground 模式运行。这看起来是开发调试用的功能,但它在生产环境有不可替代的安全价值:它让 Apache 的整个生命周期完全受rc.d框架监控,任何异常退出都会被rcorder立即捕获,并触发restart逻辑。更重要的是,它规避了daemon(3)系统调用带来的securelevel限制。在securelevel=2(FreeBSD 最高安全等级)下,daemon(3)会被内核拒绝,导致service apache24 start命令静默失败,而ps aux | grep httpd却看不到进程。如果你没开-D NO_DETACH,服务就永远起不来,你还得去翻dmesg才能找到securelevel拒绝的痕迹。
其次,apache24_httpd变量定义了 Apache 二进制文件的路径。默认是/usr/local/sbin/httpd,这没问题。但如果你为了“性能优化”而自行编译了带MPM event的版本,并把它放在/opt/apache24/bin/httpd,那么你必须显式设置apache24_httpd="/opt/apache24/bin/httpd"。否则rc.d脚本会继续调用/usr/local/sbin/httpd,而你新编译的二进制文件永远不会被加载。这个错误不会报错,只会让你以为“event MPM 已启用”,实际跑的还是默认的prefork,白白浪费了 FreeBSD 对kqueue的原生支持。
再来看apache24_pidfile。默认是/var/run/httpd.pid。这里有个致命陷阱:/var/run是内存文件系统(tmpfs),重启后内容清空。但rc.d脚本在stop阶段会读取这个 pidfile 来 kill 进程。如果 Apache 因为 segfault 崩溃,pidfile 没被清理,service apache24 stop就会尝试 kill 一个不存在的 PID,返回No such process错误,然后rc.d认为服务已停止。但其实httpd的 worker 进程可能还残留在内存里,占用端口 80/443,导致service apache24 start失败,报错Address already in use: AH00072: make_sock: could not bind to address [::]:80。解决方法不是简单地rm /var/run/httpd.pid,而是要在apache24脚本里修改pidfile的生成逻辑,让它指向一个持久化位置,比如/var/db/apache24/httpd.pid,并确保/var/db/apache24目录的 owner 是root:www,权限是0750。
最后,也是最容易被忽视的,是apache24_chrootdir。FreeBSD 的rc.d框架原生支持 chroot 启动。如果你设置apache24_chrootdir="/usr/local/www/chroot",rc.d会在启动 Apache 前自动执行chroot。但这要求你手动构建一个完整的 chroot 环境:/usr/local/www/chroot/usr/local/sbin/httpd、/usr/local/www/chroot/lib下的所有依赖库(用ldd /usr/local/sbin/httpd查)、/usr/local/www/chroot/dev/null、/usr/local/www/chroot/etc/resolv.conf……工作量巨大。不过,它带来的安全收益是质的飞跃:即使 Apache 被成功利用,攻击者也只能在 chroot 环境里活动,无法访问/etc/shadow、/root或其他服务的配置文件。我建议,对于处理敏感数据的 Apache 实例,这是必须投入时间完成的步骤。不要试图用jail替代,因为jail是进程级隔离,而chroot是文件系统级隔离,两者安全模型完全不同。
提示:验证
rc.d配置是否生效的最可靠方法,不是service apache24 status,而是sh -x /usr/local/etc/rc.d/apache24 start 2>&1 | grep -E "(flags|httpd|pidfile|chroot)"。这条命令会模拟启动过程并打印所有关键变量的值,让你一眼看清rc.d框架到底在执行什么。
3. 权限模型重构:从www用户到capsicum的最小权限实践
在 FreeBSD 12.0 上,www用户远不止是一个“运行 Web 服务的普通用户”。它是整个 Apache 安全模型的锚点,其权限配置直接决定了攻击者一旦突破 Web 层后能走多远。很多人认为把www的 home 目录设为/nonexistent、shell 设为/usr/sbin/nologin就万事大吉,这是对 FreeBSD 权限体系的严重误读。真正的硬化,是从www用户的groups、login class和capabilities三个维度进行彻底重构。
第一步,重新审视www的组成员身份。执行id www,你大概率会看到uid=80(www) gid=80(www) groups=80(www)。这太干净了,干净得危险。www用户需要读取/etc/ssl/certs下的 CA 证书,而该目录的权限是dr-xr-xr-x 2 root wheel 512。www必须属于wheel组才能读取。所以,pw groupmod wheel -m www是必须的。同理,/var/log/httpd-access.log和/var/log/httpd-error.log的默认组是www,权限是0640,这没问题。但如果你启用了mod_ssl并配置了SSLCertificateFile "/usr/local/etc/apache24/ssl/mycert.crt",那么/usr/local/etc/apache24/ssl/目录的权限必须是dr-xr-x--- 2 root www 512,且mycert.crt的权限是-r--r----- 1 root www 1234。www用户必须是该文件的 group owner,否则 Apache 启动时会报Permission denied: AH02203: Init: Can't open server certificate file。这个细节,Linux 管理员常会忽略,因为 Linux 的ssl-cert组默认包含了www-data。
第二步,为www用户创建专属的login class。FreeBSD 的login class是比ulimit更精细的资源控制机制。编辑/etc/login.conf,添加如下段落:
www|Web Server User:\ :maxproc=128:\ :cputime=3600:\ :datasize=256M:\ :stacksize=8M:\ :memorylocked=32M:\ :vmemoryuse=512M:\ :coredumpsize=0:\ :ignorenologin:\ :requirehome@:\ :tc=default:这段配置的意思是:www用户最多只能启动 128 个进程,CPU 时间总和不能超过 1 小时(防 DoS),数据段内存上限 256MB,栈大小 8MB,锁定内存 32MB(用于 SSL 密钥缓存),虚拟内存总量 512MB,核心转储被完全禁用(coredumpsize=0)。最后一行requirehome@表示强制要求www用户必须有有效的 home 目录,这能防止某些恶意脚本利用空 home 目录做临时文件存储。配置完成后,执行cap_mkdb /etc/login.conf使配置生效,然后pw usermod www -L www将www用户绑定到这个login class。这样,即使 Apache 的某个模块存在内存泄漏,它也无法耗尽系统全部内存,vmemoryuse会强制将其 kill。
第三步,也是最前沿的一步,是启用capsicum支持。capsicum是 FreeBSD 10.0 引入的轻量级能力(capability)模型,它允许进程在运行时放弃不需要的系统调用权限,从而大幅缩小攻击面。Apache 2.4.39+ 版本原生支持capsicum,但默认是关闭的。你需要在httpd.conf中添加:
<IfModule mpm_prefork_module> # Prefork MPM is required for Capsicum StartServers 5 MinSpareServers 5 MaxSpareServers 10 MaxRequestWorkers 50 MaxConnectionsPerChild 0 </IfModule> # Enable Capsicum sandboxing <IfModule mpm_prefork_module> # Drop capabilities after startup CapabilitiesDrop ALL # Keep only the absolutely necessary ones CapabilitiesKeep CAP_READ, CAP_WRITE, CAP_FSTAT, CAP_MMAP, CAP_GETPID, CAP_KQUEUE, CAP_EVENT </IfModule>注意,CapabilitiesDrop ALL并不是字面意思的“丢弃所有”,而是丢弃所有未被显式保留的 capability。上面保留的CAP_READ、CAP_WRITE等,是 Apache 处理 HTTP 请求所必需的。CAP_KQUEUE和CAP_EVENT是 FreeBSD 的高性能 I/O 多路复用机制,CAP_GETPID是为了生成唯一的请求 ID。如果你启用了mod_ssl,还需要加上CAP_CRYPTO。这个配置的效果是:一旦 Apache worker 进程开始处理请求,它就再也无法执行execve()(无法 spawn 新进程)、fork()(无法创建子进程)、socket()(无法建立新的网络连接)等高危系统调用。即使 PHP 代码里有system("ls -la"),也会直接返回Operation not permitted错误。
注意:
capsicum配置必须与mpm_prefork模块配合使用。event或workerMPM 因为使用线程,目前还不支持capsicum的完整沙箱。所以,为了最高安全级别,你必须接受prefork的内存开销,这是 FreeBSD 硬化无法绕开的权衡。
4. 配置文件的原子化拆分与权限继承链
在 FreeBSD 12.0 上,httpd.conf不应该是一个“巨无霸”配置文件。把所有东西都堆在一个文件里,不仅难以维护,更会制造出一条脆弱的权限继承链。真正的硬化,是把配置文件按功能域拆分成原子化的单元,并为每个单元精确设定其文件权限、owner 和 group,让www用户只能读取它绝对需要的部分,其余一概不可见。
标准的/usr/local/etc/apache24/目录结构如下:
/usr/local/etc/apache24/ ├── httpd.conf # 主配置,仅包含全局指令和 Include ├── extra/ │ ├── httpd-ssl.conf # SSL 配置 │ ├── httpd-vhosts.conf # 虚拟主机 │ └── httpd-mpm.conf # MPM 配置 ├── mods-available/ # 所有可用模块的 .load 文件 ├── mods-enabled/ # 符号链接到 mods-available/ └── ssl/ # 证书和密钥文件这个结构本身没问题,但默认权限是灾难性的。/usr/local/etc/apache24/目录的权限通常是drwxr-xr-x,www用户可以遍历整个目录,读取mods-available/下所有模块的.load文件内容,甚至看到ssl/目录下的证书文件名。虽然它读不到私钥(因为私钥权限是0600),但知道证书文件名本身就能泄露信息。
我的做法是进行三级权限切割:
第一级:主配置目录/usr/local/etc/apache24/
- 权限:
drwxr-x--- - Owner:
root:www - 解释:
www组可以进入目录并读取httpd.conf,但不能读取mods-available/或ssl/目录的内容,因为这两个子目录的权限会进一步收紧。
第二级:功能子目录/usr/local/etc/apache24/extra/
- 权限:
drwxr-x--- - Owner:
root:www - 解释:
www组可以读取httpd-ssl.conf等文件,但extra/目录本身不包含任何敏感数据,所以权限可以稍宽。
第三级:敏感数据目录/usr/local/etc/apache24/ssl/
- 权限:
drwx------ - Owner:
root:root - 解释:这是最严格的。
www用户完全无法进入ssl/目录,但httpd.conf中的SSLCertificateFile指令依然能正常工作,因为 Apache 主进程是以root身份启动的,它在drop privileges之前就已经读取并缓存了证书内容。www用户的 worker 进程只需要处理加密后的数据流,无需再次访问磁盘上的证书文件。
现在,最关键的是httpd.conf文件本身的权限和内容。它的权限必须是-rw-r-----,Owner 是root:www。内容必须极度精简,只保留以下几类指令:
ServerRoot,Listen,LoadModule(只加载mpm_prefork,authz_core,ssl,rewrite)User,Group,ServerAdminInclude指令,且只包含extra/*.conf和mods-enabled/*.load
所有具体的VirtualHost、Directory、Location指令,必须全部移到extra/httpd-vhosts.conf中。这样做的好处是,你可以为httpd-vhosts.conf设置不同的权限。例如,如果你的虚拟主机配置由另一个自动化工具(如 Ansible)生成,你可以把httpd-vhosts.conf的 owner 设为root:webadmin,权限设为-rw-rw----,让webadmin组的成员可以编辑,而www组只能读取。这实现了配置管理与运行时权限的分离。
还有一个极易被忽视的细节:Include指令的路径通配符。Include extra/*.conf是安全的,因为它只匹配extra/目录下的.conf文件。但Include conf/*就非常危险,因为conf/目录下可能有管理员临时存放的backup.conf或test.conf,如果这些文件里包含了SetEnv或PassEnv指令,就可能泄露环境变量。所以,Include的路径必须是绝对路径,且不使用通配符,或者只使用最窄范围的通配符。
提示:检查配置文件权限链是否稳固,可以用
httpd -t -D DUMP_RUN_CFG命令。它会输出 Apache 实际解析的配置,包括所有Include进来的文件路径。然后,对每一个路径执行ls -l <path>,确认其权限符合上述三级切割原则。任何一处不符合,都意味着权限继承链出现了漏洞。
5. 日志策略:从“记录一切”到“只记录必要”的范式转移
在 FreeBSD 12.0 上,Apache 的日志策略不是关于“如何让日志更详细”,而是关于“如何让日志本身成为一道防线”。默认的CustomLog和ErrorLog配置,会产生海量的、未经筛选的、包含潜在敏感信息的日志,这不仅消耗磁盘 I/O,更在日志文件中埋下了巨大的安全风险。真正的硬化,是实施一场日志的“范式转移”:从“记录一切”转向“只记录必要”,并用 FreeBSD 原生的syslog和newsyslog构建一个不可篡改的日志管道。
首先,废弃CustomLog的默认格式。LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined这个经典格式,会把客户端 IP (%h)、请求行 (%r)、Referer 和 User-Agent 全部记下来。其中,%r可能包含完整的 URL 查询参数,如果应用有登录接口,?username=admin&password=123456就会明文出现在日志里。解决方案是自定义一个“硬化格式”:
LogFormat "%a %t \"%m %U\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" hardened CustomLog "|/usr/local/bin/rotatelogs -l /var/log/httpd-access.log.%Y%m%d 86400" hardened这里的关键变化是:%h换成了%a。%a是 Apache 2.4 引入的“实际客户端地址”,它会自动跳过X-Forwarded-For等伪造头,只记录 TCP 连接的真实源 IP。%U是请求的 URL 路径,不包含查询字符串 (%q),这就避免了密码等敏感参数入日志。%m是 HTTP 方法,%U是路径,组合起来就是GET /login,足够用于流量分析,又不泄露凭证。
其次,ErrorLog必须重定向到syslog。ErrorLog /var/log/httpd-error.log是最危险的配置,因为www用户拥有对该文件的写权限,它可以被恶意脚本覆盖或追加任意内容。正确的做法是:
ErrorLog syslog:local7 LogLevel warnsyslog:local7表示将错误日志发送到syslog的local7设备。然后,在/etc/syslog.conf中添加:
local7.* /var/log/httpd-error.log这样,日志的写入者就变成了syslogd进程(以root身份运行),www用户完全无法干预日志内容。syslogd还支持远程日志转发,你可以轻松地把local7的日志发送到一个独立的、只读的中央日志服务器,实现日志的物理隔离。
第三,日志轮转必须由newsyslog接管,而非rotatelogs。rotatelogs是 Apache 自带的轮转工具,但它以www用户身份运行,存在被劫持的风险。newsyslog是 FreeBSD 原生的、由root运行的日志轮转守护进程。配置/etc/newsyslog.conf:
/var/log/httpd-access.log www:www 640 7 * $D0 JN /var/run/httpd.pid /var/log/httpd-error.log root:wheel 644 7 * $D0 JN /var/run/syslogd.pid这里,httpd-access.log的 owner 是www:www,因为rotatelogs进程需要写入;而httpd-error.log的 owner 是root:wheel,因为syslogd写入。JN标志表示使用zlib压缩旧日志,$D0表示每天轮转一次。/var/run/httpd.pid是为了让newsyslog在轮转后向httpd发送SIGUSR1信号,通知它重新打开日志文件。
最后,也是最重要的,是启用mod_security的日志审计功能,但要将其日志与主日志分离。mod_security的SecAuditLog默认也写入error_log,这会造成日志污染。你应该单独配置:
SecAuditLog /var/log/modsec_audit.log SecAuditLogType Serial SecAuditLogRelevantStatus "^(?:5|40[1-9])"然后,同样用newsyslog管理/var/log/modsec_audit.log,并设置其权限为0600,owner 为root:www。这样,mod_security的审计日志就成为一个独立的、高价值的安全事件源,可以被 SIEM 工具专门采集分析,而不会和普通的访问日志混在一起。
注意:
mod_security的规则集必须针对 FreeBSD 进行微调。例如,SecRule REQUEST_HEADERS:User-Agent "@rx (?i)(sqlmap|nikto|dirbuster)" "phase:1,deny,status:403,msg:'Blocked SQL Injection Scanner'"这条规则在 FreeBSD 上可能因正则引擎的差异而误报。我建议,先用SecRuleEngine DetectionOnly运行一周,收集modsec_audit.log,再根据真实流量调整规则,而不是直接On。
6. TLS/SSL 配置:超越SSLCipherSuite的 FreeBSD 原生加固
在 FreeBSD 12.0 上配置 HTTPS,绝不仅仅是复制粘贴一段SSLCipherSuite就能搞定的。FreeBSD 的 OpenSSL 库、内核的crypto框架以及libssl的编译选项,共同构成了一个独特的 TLS 加固环境。很多管理员花大力气配置了完美的CipherSuite,却忽略了 FreeBSD 特有的SSLSessionCache和SSLStaplingCache的底层实现,导致 TLS 性能低下,甚至引发安全漏洞。
首先,SSLSessionCache的配置必须与 FreeBSD 的shm(共享内存)机制对齐。默认的SSLSessionCache "shmcb:/usr/local/var/run/ssl_scache(512000)"使用shmcb(shared memory circular buffer)后端,它依赖于sysvshm。但在 FreeBSD 12.0 上,sysvshm的默认最大共享内存段大小是4MB(kern.ipc.shmmax=4194304)。如果你的ssl_scache文件大小设为512000字节(约 500KB),看起来很安全,但shmcb实际分配的内存是按页对齐的,最终会占用1MB的sysvshm段。如果同时运行多个 Apache 实例,很容易耗尽sysvshm,导致SSLSessionCache失效,TLS 握手退化为全握手,CPU 使用率飙升。解决方案是改用dc(distributed cache)后端,它基于memcached,不依赖sysvshm:
SSLSessionCache dc:127.0.0.1:11211前提是,你已经安装并启用了memcached服务(pkg install memcached && sysrc memcached_enable="YES" && service memcached start)。dc后端的性能和可靠性,在 FreeBSD 上远超shmcb。
其次,SSLStaplingCache的配置必须考虑OCSP响应的验证开销。SSLStaplingCache "shmcb:/usr/local/var/run/ocsp(128000)"同样面临sysvshm限制。更重要的是,SSLStaplingResponderTimeout的默认值是10秒,这意味着如果 OCSP 响应器(如 Let's Encrypt 的http://ocsp.int-x3.letsencrypt.org)响应缓慢,Apache 会阻塞整个 TLS 握手长达 10 秒,用户体验极差。我的经验是,将超时设为3秒,并启用SSLStaplingReturnResponderErrors off,这样即使 OCSP 响应器暂时不可用,Apache 也会返回一个“软失败”的 stapling 响应,而不是阻塞连接:
SSLStaplingCache "shmcb:/usr/local/var/run/ocsp(128000)" SSLStaplingResponderTimeout 3 SSLStaplingReturnResponderErrors off SSLStaplingFakeTryLater off第三,SSLCertificateChainFile已被废弃,必须使用SSLCACertificatePath。FreeBSD 的openssl命令行工具,其ca-bundle.crt文件的格式与 Linux 不同。Linux 的ca-bundle.crt是一个大文件,包含所有 CA 证书;而 FreeBSD 的/usr/local/share/certs/ca-root-nss.crt是一个符号链接,指向/usr/local/share/certs/ca-root-nss.pem。你必须确保SSLCACertificatePath指向一个目录,而不是一个文件:
SSLCACertificatePath "/usr/local/share/certs/"并且,该目录下的所有.crt文件必须是 PEM 格式,且文件名必须是其证书的哈希值(openssl x509 -hash -noout -in cert.crt生成)。c_rehash /usr/local/share/certs/命令会自动完成这个操作。如果SSLCACertificatePath配置错误,mod_ssl就无法验证客户端证书,导致双向认证失败。
最后,SSLProtocol的配置要结合 FreeBSD 的内核特性。SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1是常见写法,但它在 FreeBSD 12.0 上有一个隐藏问题:TLSv1.2是唯一被完全支持的协议,但TLSv1.3在 FreeBSD 12.0 的 OpenSSL 1.1.1 中是实验性的,且需要内核net.inet.tcp.delayed_ack参数的配合。我的建议是,明确指定SSLProtocol TLSv1.2,并禁用所有旧协议,而不是用all -xxx这种模糊语法。同时,启用SSLHonorCipherOrder on,确保服务器的密码套件优先级高于客户端的,这是抵御降级攻击的关键。
提示:验证 TLS 配置是否生效,不要只用
openssl s_client -connect yourdomain.com:443。要用curl -I --tlsv1.2 https://yourdomain.com和curl -I --tlsv1.3 https://yourdomain.com分别测试,观察返回的HTTP/2或HTTP/1.1协议版本,以及curl的输出中是否有ALPN, offering h2字样。这才是真实的 TLS 握手结果。
7. 模块精简与动态加载:砍掉所有“可能用到”的模块
在 FreeBSD 12.0 上,Apache 的模块列表不是“功能越多越好”,而是“越少越安全”。每一个被LoadModule加载的模块,都是一个潜在的攻击面。很多管理员为了“以防万一”,会把mod_php7、mod_perl、mod_python、mod_wsgi全部加载,结果是,只要其中一个模块存在一个未公开的 0day 漏洞,整个 Apache 服务就岌岌可危。真正的硬化,是奉行“零信任”原则:只加载当前业务绝对必需的模块,其余一律卸载。
httpd -M命令会列出所有已加载的模块。在一台刚安装的 FreeBSD 12.0 Apache 上,你可能会看到 40+ 个模块。我的目标是将其压缩到 12 个以内。以下是经过严格筛选的“黄金十二模块”清单及其不可替代的理由:
mpm_prefork_module:capsicum沙箱的唯一支持者,无可替代。authz_core_module:所有授权指令(Require all granted)的基础。authz_host_module:基于 IP 的访问控制(Require ip 192.168.1.0/24)。log_config_module:CustomLog和ErrorLog的基础。mime_module:AddType和TypesConfig的基础。setenvif_module:SetEnvIf指令,用于根据请求头设置环境变量。ssl_module:HTTPS 的基石。rewrite_module:URL 重写,几乎所有现代 Web 应用都需要。headers_module:Header set指令,用于设置安全头(X-Frame-Options)。expires_module:ExpiresActive,用于缓存控制。deflate_module:Deflate,用于压缩响应体,减少带宽。security2_module:mod_security的主模块,WAF 的核心。
所有其他模块,如mod_php7、mod_proxy、mod_cache、mod_disk_cache、mod_socache_shmcb,都应该被注释掉。特别是 `mod