XSS攻击的核心在于浏览器将攻击者注入的字符串当作HTML或JavaScript执行。Apache作为通用的HTTP服务器,默认只负责静态文件与反向代理,并不会解析请求参数中的恶意脚本。因此,要让Apache具备XSS过滤能力,必须引入一个工作在请求处理阶段的WAF模块。mod_security(又称ModSecurity)是目前Apache生态中最成熟的方案,它通过自定义规则语言对HTTP请求的各个部分进行正则匹配、黑名单检测和异常评分,拦截结果可以直接返回403或重定向。接下来从环境准备讲起,逐步构建一套可用的XSS过滤策略。

mod_security安装与基础配置
mod_security的安装方式取决于操作系统。在Debian或Ubuntu上,可以直接通过apt安装官方打包的模块,并且最新的Apache 2.4已经默认支持动态模块加载。执行apt install libapache2-mod-security2之后,启用模块a2enmod security2并重启Apache。CentOS或RHEL系列则需要启用EPEL仓库,安装mod_security软件包,然后在/etc/httpd/conf.modules.d/目录下确认LoadModule security2_module modules/mod_security2.so这一行存在。安装完成后,Apache的error_log中会出现ModSecurity的启动信息,表示模块已经挂载。
单纯的模块加载不会过滤任何请求,因为规则尚未配置。mod_security有两种工作模式:传统模式(DetectionOnly)和主动拦截模式(On)。建议在初始调试阶段使用DetectionOnly,让规则只记录不阻断,观察误报情况后再切换为On。核心配置文件通常放在/etc/modsecurity/modsecurity.conf,里面需要设置SecRuleEngine On、SecRequestBodyAccess On、SecResponseBodyAccess Off(XSS过滤主要针对请求,响应体过滤对性能影响大)以及SecAuditLog /var/log/apache2/modsec_audit.log。这些指令控制着引擎行为,务必逐项核对。
为了快速获得一套经过社区验证的规则,可以下载OWASP核心规则集(CRS)。CRS提供了针对SQL注入、XSS、命令注入等常见攻击的通用检测规则,安装方式为解压后配置Include /path/to/crs/*.conf。但CRS规则偏向通用性,对特定业务场景的XSS过滤往往不够精准,因此理解SecRule语法并编写自定义规则才是关键。
SecRule指令的XSS检测原理
SecRule是mod_security的核心指令,一条规则由变量、运算符和动作三部分组成,格式为SecRule VARIABLES OPERATOR [ACTIONS]。变量指定检查目标,例如ARGS表示所有请求参数(GET和POST合并),REQUEST_URI表示URL路径,REQUEST_HEADERS:User-Agent表示特定请求头。XSS攻击载荷通常出现在URL查询串、POST表单字段、Cookie甚至HTTP头中,所以常用ARGS、REQUEST_COOKIES、REQUEST_HEADERS组合。运算符决定匹配方式,@rx代表正则表达式,@pm代表多模式字面量匹配(性能优于大量@rx),@streq表示字符串完全相同。动作则定义匹配后的处理,例如deny阻断、log记录、status:403返回状态码、msg自定义日志消息。
XSS检测的核心思路是寻找JavaScript语法特征。典型的攻击向量包括:<script>标签、事件处理属性如onerror=、onload=、onclick=,以及javascript:伪协议。一条最简单的规则可以写成:
SecRule ARGS "@rx <script>" "id:100001,phase:2,deny,status:403,msg:'XSS script tag detected'"
但攻击者会使用大小写混合、编码绕过、标签嵌套等方式规避,例如<ScRiPt>、<scr、<img src=x onerror=alert(1)>。因此正则表达式必须覆盖大小写不敏感(使用(?i)修饰符)并考虑HTML实体编码。mod_security提供@validateByteRange和@detectXSS等专用运算符,但@detectXSS依赖内置的libinjection库,检测效果较好且误报较低,推荐优先使用。
SecRule ARGS "@detectXSS" "id:100002,phase:2,deny,status:403,log,msg:'Possible XSS attack detected'"
使用@detectXSS的好处在于它基于语法解析而非简单正则,能够识别alert(1)、document.cookie等函数调用模式,同时避免对普通文本中的尖括号产生误报。但需要注意,该运算符只作用于单个参数值,如果攻击载荷分散在多个参数中则需要结合其他规则。
编写自定义XSS过滤规则实战
针对常见的XSS向量,可以编写一组精细化的正则规则。以下示例展示了如何同时检测script标签、事件属性和伪协议,并利用变量集合减少重复规则。第一条规则检测GET和POST参数中是否包含不区分大小写的script标签:
SecRule ARGS "@rx (?i)<script[^>]*>" "id:100101,phase:2,deny,status:403,log,msg:'XSS attack: script tag in arguments'"
事件属性是另一种高频攻击方式,最常见的形式如<img src=1 onerror=prompt(1)>。攻击者可能会把onerror写成onErRoR、ONERROR甚至用换行符插入。使用正则(?i)on(load|error|click|mouseover|focus|blur)\s*=可以覆盖多数情况,其中\s*用于容忍属性名与等号之间的空格或换行。
SecRule ARGS "@rx (?i)on(load|error|click|mouseover|focus|blur)\s*=" "id:100102,phase:2,deny,status:403,log,msg:'XSS attack: event handler attribute'"
javascript伪协议常见于链接或iframe的src属性,例如javascript:alert(document.cookie)。由于URL编码中:可能被写成%3A,mod_security在phase:2阶段已经对参数做过URL解码,所以正则可以直接匹配。规则如下:
SecRule ARGS "@rx (?i)javascript\s*:" "id:100103,phase:2,deny,status:403,log,msg:'XSS attack: javascript protocol'"
这三条规则组合起来可以拦截绝大多数反射型XSS。但业务中可能存在富文本编辑器场景,用户需要提交带HTML格式的内容,此时全局拦截会导致严重的误报。处理方式有两种:一是针对特定参数关闭XSS规则,二是使用白名单标签过滤(需要额外的HTML解析模块)。对于第一种,mod_security允许通过SecRuleUpdateTargetById排除指定参数,例如允许description字段包含HTML:
SecRuleUpdateTargetById 100101 "!ARGS:description" SecRuleUpdateTargetById 100102 "!ARGS:description" SecRuleUpdateTargetById 100103 "!ARGS:description"
这条指令会在运行时修改指定ID规则的检查目标,将description参数从变量集合中移除。注意该指令必须放在SecRule定义之后,且ID必须匹配。对于富文本场景,更安全的做法是先在后端进行HTML标签白名单过滤,再让mod_security只检查剩余危险属性。
误报处理与规则调优
XSS过滤规则上线初期几乎必然出现误报,常见的误报来源包括:用户长文本中包含的数学表达式(如1 < 2)、编程语言代码片段、传感器数据中的特殊符号等。处理误报的核心工具是mod_security的审计日志。在modsecurity.conf中启用SecAuditEngine RelevantOnly和SecAuditLogType Serial,每次触发规则都会写入一个独立的日志文件(或同一个文件分段),其中包含请求头、请求体、匹配的规则ID和消息。通过分析日志可以快速定位是哪条规则、哪个字段引发了拦截。
定位后,优先使用SecRuleUpdateTargetById进行精准排除,而不是直接删除规则。如果误报集中在某个URL路径,可以利用chain动作将规则限定在特定路径下。例如只对/search接口启用script标签检测:
SecRule REQUEST_URI "@beginsWith /search" "id:100110,phase:2,chain"
SecRule ARGS "@rx (?i)<script[^>]*>" "deny,status:403,msg:'XSS in search'"链式规则只有在第一条条件满足时才会评估后续规则,从而提高匹配精度。此外,mod_security支持SecRuleRemoveById在特定位置禁用某条规则,以及SecRuleUpdateActionById修改规则动作(例如将deny改为log)。这些调优手段需要结合业务流量的实际特征,建议在灰度环境验证后再应用到生产。
测试与验证方法
部署规则后需要进行系统测试。最简单的方式是使用curl发送包含XSS载荷的请求,观察返回状态码和错误日志。例如:
curl -i "http://localhost/search?q=<script>alert(1)</script>"
如果规则生效,服务器应返回403 Forbidden,并且/var/log/apache2/error.log中出现ModSecurity: Access denied with code 403字样,同时审计日志中记录了完整的载荷。为了测试事件属性向量,可以发送如下请求:
curl -i -X POST -d "name=<img src=x onerror=alert('xss')>" "http://localhost/comment"除了手动测试,还可以集成自动化扫描工具(如Nikto、Wapiti或自研脚本)批量发送常见的XSS payload,统计拦截率。需要注意的是,绕过测试同样重要——攻击者可能使用HTML实体编码(<script>)、混合大小写、空字节、双重URL编码等手段。mod_security在phase:2阶段已经对参数值做了一次解码,但某些复杂编码可能还需要借助@unconditionalMatch或自定义解码函数。对于安全性要求极高的系统,建议在mod_security之后保留一层应用层输出编码,形成纵深防御。
最后强调性能影响。每条正则规则都会增加每个请求的CPU开销,尤其是使用@rx且模式复杂的场景。优化方向包括:优先使用@pm字面量匹配(支持多模式并行扫描,性能远高于正则)、将规则限制在必要的phase(XSS过滤主要在phase:2请求体阶段)、对静态资源(如图片、CSS、JS文件)通过SecRule REQUEST_FILENAME "@rx \.(css|js|png|jpg|gif)$" "phase:1,allow"跳过检查。通过合理的分层与调优,mod_security在Apache上可以在几乎不影响响应时间的前提下实现可靠的XSS防护。
Apache XSS跨站脚本过滤mod_security修改时间:2026-09-19 16:22:18