Web服务器被入侵,攻击者拿到一个Webshell,往往只是噩梦的开始。真正可怕的是,这个Web进程随后可以随意读取/etc/passwd、遍历目录、甚至尝试提权。默认的Linux自主访问控制(DAC)模型下,进程继承了启动者(如www-data或apache)的全部权限,一旦Web应用存在漏洞,整个系统的防线就形同虚设。要堵住这个缺口,必须引入强制访问控制(MAC),而SELinux正是Linux生态中最成熟、最强大的MAC实现。

理解SELinux的类型强制(Type Enforcement)

SELinux的核心不是简单的“允许”或“拒绝”,而是基于安全上下文的类型强制。每个主体(进程)和客体(文件、端口、设备)都被打上标签,策略定义了不同类型之间允许的操作。Web进程的标签通常是httpd_t,Web内容目录的标签通常是httpd_sys_content_t。默认情况下,httpd_t只能读取httpd_sys_content_t类型的文件,即使文件权限是0777,SELinux也会强制拦截。这种机制从根本上限制了漏洞被利用后的横向移动能力。

查看和诊断SELinux对Web进程的限制

很多运维遇到权限问题时,第一反应是setenforce 0关闭SELinux,这等于发现烟雾报警器响了就拆电池。正确的做法是读懂审计日志。使用ausearch命令可以快速定位被拦截的操作:

ausearch -m avc -ts recent

输出中会明确告诉你哪个进程(scontext)试图访问哪个资源(tcontext),以及具体的操作(如read、write、connect)。更直观的工具是sealert,它能分析审计日志并给出修复建议,甚至直接提供生成策略模块的命令。永远不要因为Web应用报错就直接关闭SELinux,先用这些工具诊断,你会发现90%的情况是标签打错了,而不是策略本身有问题。

为Web内容目录正确设置文件上下文

最常见的场景是自定义的Web根目录。如果你把网站文件放在/opt/web或/home/user/public_html下,默认标签是usr_t或user_home_t,httpd_t进程根本无法读取。需要手动设置上下文:

semanage fcontext -a -t httpd_sys_content_t "/opt/web(/.*)?"
restorecon -Rv /opt/web

这里的关键是semanage命令会永久修改系统的文件上下文数据库,restorecon根据这个数据库恢复标签。直接用chcon也能临时改标签,但文件系统检查或重新标记时会丢失,所以必须用semanage持久化。如果Web应用需要写入某些目录,比如上传目录或缓存目录,需要设置为httpd_sys_rw_content_t:

semanage fcontext -a -t httpd_sys_rw_content_t "/opt/web/uploads(/.*)?"
restorecon -Rv /opt/web/uploads

同时还要开启对应的布尔值,允许httpd进程写入这类目录:

setsebool -P httpd_unified 1
setsebool -P httpd_builtin_scripting 1
限制Web进程的网络访问权限

Webshell反弹连接是攻击者的常用手法。默认SELinux策略中,httpd_t的进程网络权限是被严格限制的。httpd_can_network_connect布尔值默认关闭,意味着Apache或Nginx的Worker进程无法主动向外发起TCP连接。这能直接阻断绝大多数反弹Shell。如果你的Web应用确实需要连接外部服务,比如调用第三方API或连接后端数据库,应该精确开启:

setsebool -P httpd_can_network_connect on

但更安全的做法是只允许连接特定端口。比如只允许连接本地的Redis或MySQL,可以编写自定义策略模块,而不是完全放开网络权限。如果应用需要连接远程数据库,至少应该限制目标地址和端口,而不是一刀切允许所有出站连接。

隔离不同Web应用之间的权限

在同一台服务器上运行多个Web应用时,默认所有httpd_t进程权限相同,一个应用被攻破就能读到另一个应用的配置文件。通过多类别安全(MCS)可以实现应用间隔离。给不同应用打上不同的MCS标签:

semanage fcontext -a -t httpd_sys_content_t -r s0:c1 "/var/www/app1(/.*)?"
semanage fcontext -a -t httpd_sys_content_t -r s0:c2 "/var/www/app2(/.*)?"

这样app1的httpd进程即使被拿下,也无法读取类别为c2的文件。这需要配合虚拟主机或独立的PHP-FPM池来实现,每个池的进程运行在不同的MCS上下文中。这是大型托管平台常用的隔离手段,远比依赖PHP的open_basedir可靠。

编写自定义策略模块精确控制Web进程行为

通用策略无法覆盖所有场景,但直接修改策略源码再编译太复杂。audit2allow工具可以将审计日志中被拒绝的操作转换为策略模块:

grep httpd /var/log/audit/audit.log | audit2allow -M mywebapp

这会生成mywebapp.te和mywebapp.pp文件。但千万不要直接安装生成的模块,必须先审查.te文件。audit2allow只是机械地转换,它不知道这些操作是否合理。审查时要问自己:这个进程真的需要访问这个文件吗?真的需要这个网络权限吗?确认无误后再加载:

semodule -i mywebapp.pp

如果发现生成的策略过于宽泛,比如允许httpd_t访问任意文件,应该手动修改.te文件,将通配符替换为具体的路径或类型,然后再编译安装。这种精细化的策略定制是SELinux真正发挥威力的地方。

使用SELinux沙盒隔离高风险Web组件

对于处理用户上传文件、运行脚本或解析复杂格式的Web组件,可以使用sandbox工具将其运行在严格的隔离环境中。SELinux的sandbox利用MCS和类型强制,创建一个临时目录,进程只能访问该目录,无法触碰系统其他部分。比如对用户上传的图片进行转换处理:

sandbox -t sandbox_web_t -i input.jpg -o output.jpg -- convert input.jpg -resize 800x600 output.jpg

即使ImageMagick存在漏洞被利用,攻击者也被困在沙盒中,无法访问真实文件系统。这种机制比依赖容器或虚拟机轻量得多,适合在Web应用内部对不可信数据进行处理时使用。

监控和验证SELinux策略的有效性

策略部署后不是一劳永逸。定期使用seinfo、sesearch分析当前加载的策略,确认没有意外的权限开放。使用setroubleshootd的日志分析功能,持续监控被拒绝的操作。如果发现大量正常的业务操作被拒绝,说明策略需要调整;如果发现异常的拒绝模式,比如httpd_t试图访问/etc/shadow,那就是入侵信号。将SELinux审计日志接入集中日志平台,设置告警规则,让SELinux从被动防御变成主动的入侵检测系统。

SELinux的强制访问控制是Web服务器安全配置中不可绕过的核心环节。它不能替代代码审计、输入验证或及时打补丁,但它提供了最后一道防线,当所有上层防御都失效时,策略引擎仍然在底层强制约束进程行为。正确配置的SELinux策略能让一个被完全控制的Web进程依然无法对系统造成实质性破坏,这才是纵深防御的真正意义。