本文目录导读:

- 核心原则:先建白名单,后封黑名单
- 第一部分:如何写好一条规则?(黄金四要素)
- 第二部分:实战规则写法(从基础到高级)
- 第三部分:常见场景的“有效”规则速查表
- 第四部分:写完后,如何验证有效性?
- 总结:一条“有效”的WAF规则长什么样?
写一份有效的WAF(Web应用防火墙)规则,核心不在于“写得多”,而在于“精准”、“低误报”和“适应业务”,以下是编写有效WAF规则的实战原则和示例。
核心原则:先建白名单,后封黑名单
- 无效的WAF:一上来就写“禁止SQL注入”、“禁止XSS”这种宽泛的规则,结果把正常用户写的“文章标题带单引号”给拦截了。
- 有效的WAF:先梳理业务,明确哪些是正常流量,针对非正常部分做精确限制。
第一部分:如何写好一条规则?(黄金四要素)
一条有效的规则通常包含以下四个维度的组合:
- 源(Source): IP地址、国家、ASN、请求来源。
- 请求特征(Request): URI(访问路径)、Header(头部)、Body(请求体)、HTTP方法(GET/POST/PUT)。
- 匹配模式(Pattern): 正则表达式、字符串包含、长度限制、频率统计。
- 动作(Action): 监控(Log)、放行(Allow)、拦截(Block)、挑战(Challenge/JS验证)、限速(Rate Limit)。
第二部分:实战规则写法(从基础到高级)
语法级:精确匹配 > 正则 > 通用规则
- 错误写法:
if body contains "select" then block(会把“select your option”这种正常页面也封掉) - 有效写法:
if body matches “/[\b(SELECT|UPDATE|DELETE)\b]\s+\b(FROM|SET|INTO)\b/i” then block(要求必须有SQL关键字组合)
防护级:建立多道防线
路径级白名单(最有效)
- 规则示例:
/api/login路径只允许application/json的POST请求,其余统统拦截。 - 写法:
if request.method == "POST" and request.uri starts with "/api/user/" and request.content-type != "application/json" then block
参数级强校验
- 规则示例:
/user?id=abc应该被拦截,因为id参数应该是数字。 - 写法:
if request.uri matches "^/user\?id=" and request.args.id matches "^[a-zA-Z]+$" then block
动态规则(机器学习/防护)
- 原理:不写死规则,而是建立一个“正常行为基线”。
- 示例:某个API在3分钟内正常请求是10次,突然达到1000次,触发限速。
绕过防护:必须考虑的4种情况
避免被绕过,是有效规则的关键:
- 大小写混淆:
SeLeCt。 → 对策:规则统一tolower()处理。 - 双重编码:
%25%32%37(URL编码后的单引号)。 → 对策:先做一次urldecode再匹配。 - 注释拆解:
SEL/**/ECT。 → 对策:正则中匹配\W*\b跳过注释符号。 - 协议模糊: 在Multipart上传的body里藏payload。 → 对策:确保WAF能解析
multipart/form-data。
第三部分:常见场景的“有效”规则速查表
| 场景 | 无效/危险的写法 | 有效/精准的写法 |
|---|---|---|
| XSS攻击 | if body contains "<script>" then block |
if body matches “/<(script|img|link|iframe)[\s]+[^>]*?(onerror|onload|onclick)=/i” then challenge (增加JS验证) |
| SQL注入 | if body contains “'” then block |
if request.uri matches “/search?” and args.keywords matches “/\b(SELECT|UNION|INSERT)\b/i” then block |
| CC攻击 | if rule rate > 100 then block |
if client.ip rate per 60s for uri: /api/register > 20 then challenge (对敏感路口单独限速) |
| 扫描器 | if user-agent contains “sqlmap” then block |
if user-agent not in [“Mozilla/5.0...”, “curl/7...”, “PostmanRuntime/...”] 且 request.path matches “/(admin|phpinfo|\.env)” then challenge |
第四部分:写完后,如何验证有效性?
规则的失效往往发生在“上线之后”,建议按以下步骤验证:
-
部署模式:先监控,后拦截
- 新规则刚开始设为
Log模式(只记录不拦截),观察1-2天,看是否有正常请求被误报。 - 指标:误报率 < 0.01% 才算合格。
- 新规则刚开始设为
-
压力测试
- 使用
OWASP ZAP或SQLMap(加上--random-agent和--tamper=space2comment等绕过参数)测试。 - 如果被绕过,说明规则不够“严格”,需要增加更多特征(如:请求时间、参数长度、Cookie值)。
- 使用
-
持续迭代
- 无效规则的特征:每天产生大量误报(正常用户投诉)或者完全0命中(可能被绕过了)。
- 有效规则的特征:每周命中量稳定,误报极少,且能捕获利用
0day漏洞的请求。
一条“有效”的WAF规则长什么样?
规则名称:精确的、针对特定API的参数校验规则
请求条件:http.request.uri eq "/api/v1/order"
匹配条件:http.request.body.args.total_price not matches "^[0-9\.]+$"
防护行为:block (返回403)
附加策略:同时对该端点的同IP请求限速每分钟30次
模式:监控运行72小时无误报后,正式启用
最后记住:WAF是药,不是饭,最好的防护是代码层没有漏洞,WAF只是兜底。 如果业务代码本身有高危风险(如SQL拼接),再好的WAF规则也只能防住90%,总有漏网之鱼。