
企业在部署企业级WAF的时候经常会遇到这样的困境:如果规则太严格的话就会阻止富文本编辑器、搜索框和低代码表单中正常的输入;而规则太宽松又会使得union select或者<script>等载荷直接穿过。曾经有一个金融平台因为XSS规则命中了“评论含“”而导致UGC社区发帖失败,还有一个电商平台由于SQL词法规则把“1′ or 1’1”这样的书籍标题误认为是注入攻击。调整并不是关闭防护系统,而是将“攻击识别”由简单的字符串匹配提升到基于上下文的判断。
首先根据接口来确定优先级,不能一概而论
根据业务的影响来划分流量:
- 交易、登录和支付接口:高拦截、低容忍,启用语义引擎+虚拟补丁
- 搜索、筛选、富文本:观察模式跑7天,先告警不阻断
- 内部运营后台:根据IP和认证凭证建立白名单来限制规则的适用范围
不能把所有的URI都放入“全局严格模式”,接口级别的政策可以减少误报。
SQL注入由正则表达式转向了语义和上下文中
传统的规则依靠union|select|sleep\(这样的特点来判断,很容易造成误判:
- 商品名称为“三星SLEEP款床垫”。
- 简历字段“1 OR 1=1 面试题”
实际操作中:
- 参数级别解构:给
id和uid加上类型限制(只接受整数,不接受其他类型) - 语义引擎兜底:判断输入是否构成完整的SQL语法树,而不是单个关键词
- 编码归一化:首先对URL编码、Unicode和双重编码进行处理,然后再进行检测以防止Ghost Bits类的绕过
- 数据库方言适配:为不同的数据库系统(如MySQL、PostgreSQL和Oracle)分别制定相应的规则来避免跨系统的错误判断
XSS根据响应上下文进行放行,并不随意拦截标签
富文本、Markdown和客服对话中本身就带有标签,所以直接阻止<script>是合理的,但是也会影响到<b>以及onclick等合法的属性设置。
调优要点:
- 把“存储型入口”与“展示出口”分开来:入口用标签白名单的方式处理,出口用HTML实体转义的方式来处理。
- 参数维度放行:
content、bio、reply使用富文本方式处理,而q、keyword则采用纯文本的方式 - 框架解析对齐:模拟后端参数合并逻辑,防止参数污染绕过(例如
q=1'&q=alert(1)拼接之后被执行) - 分别建立
style、svg、math的模型来防止只扫描<script>而遗漏了事件句柄
把“观察、基准、例外”代替掉人工拍脑袋加白
- 全部日志抽样:统计前1000个错误报警的参数名称和接口
- 建立业务基准:正常的长度、字符集、结构模板(JSON/表单/Multipart)
- 精确的例外情况是只对特定的接口、参数和字段模式进行白名单设置,并不是全部开放
- 置信度分级:低置信告警进入SIEM系统,高置信则直接进行拦截
智能白名单应该和“URI+参数+数据类型”一起使用,不能对整个网站进行开放。
与RASP以及应用层验证一起构成一个完整的体系
WAF只考虑了流量问题而没有考虑到运行时的语义问题,在重要的接口上进行了叠加:
- RASP可以监测到SQL注入和模板漏洞,在WAF拦截之后进行二次防护
- 应用层预检查:强类型、ORM参数化、输出编码
- 用虚拟补丁来代替0day期,不能暂时关闭规则
线上验证指标
调整之后有四个数字:
- 攻击拦截率(脱敏靶场自测)
- 业务误杀率(正常请求被拦截的比例小于0.1%)
- 规则命中分布(防止某一条规则造成80%以上的错误报警)
- 灰度回滚的时间长度(可以按域名进行秒级的降级操作)
总结
WAF调优的核心就是把“安全规则”转成“业务语言”,即知道哪些接口可以碰SQL、哪些字段可以放HTML、哪些输入只是表面上看起来像攻击。用正则表达式粗浅地拦截一定会误伤,而通过接口分级、语义分析、上下文建模以及基线学习的方式,在SQL注入和XSS高压防护的同时把正常的请求全部放行出去。