尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
WAF绕过实战:从SQL注入发现到应用层加固
上个月我帮某公司做了一次授权范围内的安全测试目标是他们一个自研的业务查询系统前端套了一款开源的WAF组件规则集是默认的。第一天我按常规思路测一个名为“报表查询”的接口参数report_id拼接SQL的迹象很明显我把带and sleep(3)的时间盲注请求发过去WAF立刻吐出一整页告警响应码直接变成了403。按很多人的经验到这里就可以写“WAF防护有效”收工了。但做过几年应用安全的人都清楚WAF拦截一次不代表能拦截所有情况尤其SQL注入这种已经存在二十年的问题变体和编码组合几乎是无穷尽的。从那天下午开始我在本地把目标系统的镜像搭好花了三天时间反复构造、验证、绕过最终用几个看起来完全不像攻击特征的请求穿过了WAF的拦截一路拿到了后端数据库的权限。这篇文章就把整个测试过程、思路背后的原理以及最后怎么把这些经验反哺成防御措施完整记录下来。做Web安全的朋友、负责应用加固的开发者应该都能从中拿到一套可以复用的方法论。1. 授权测试实录Payload被拦后的第一反应1.1 注入点初探从报错回显确认问题最先做的是常规的参数收集。目标系统是一个基于某老旧PHP框架搭建的报表模块我拿到授权后第一步只做信息枚举把所有接收用户输入的参数列出来包括查询关键字、排序字段、分页参数、以及report_id这类看起来像数字主键的参数。大部分参数都表现正常唯独report_id不对劲。测试方式很克制先在参数后面追加一个单引号观察页面是否有异常。很多管理员会问这不就相当于在主动攻击吗但这类写法在授权测试里只是验证“输入点是否进入SQL上下文”的标准动作单引号进去后页面直接输出了一个SQL语法错误片段错误信息里甚至带出了部分SQL上下文。这说明了两件事第一report_id确实被拼进了SQL语句第二应用没有屏蔽数据库的报错输出——这本身已经是第一个安全隐患。确认了注入点之后我并没有急着取数据而是按测试计划先做无害验证把report_id1和report_id2返回的结果做对比确认通过参数能控制查询结果。这一步让后面的所有验证都有了清晰基线也避免一上来就发出高风险的攻击请求。1.2 先分清楚拦截来自WAF还是应用代码常规的时间盲注Payload一上去就被拦截这反而让我松了口气——说明WAF确实在工作。但接下来的第一件事不是想怎么绕而是先确认拦截到底发生在哪一层。判断错了方向后面全是白忙。我是这样区分的特征WAF拦截应用层过滤/校验响应状态码403、406等特殊状态多为200或400响应内容固定告警页、规则ID、标识字段业务预期的错误提示响应头常带WAF自定义响应头无明显额外头拦截后应用日志通常没有对应访问记录会留下应用层日志我用抓包工具看了响应头和响应体确认是WAF拦的。它返回的拦截页带有规则编号和明文特征明显是规则引擎命中而不是应用代码里的参数判断。这个判断看起来基础但在真实项目里我见过不止一个测试人员把WAF拦截当成“应用有过滤”或者反过来把应用过滤当成“WAF探测”最后排查方向完全跑偏。确认是WAF后我多做了几个对比请求发一条完全正常的report_id1观察WAF是否放行再发一条只带union select的请求观察命中特征的情况。结果正常请求通行无阻带关键字的请求立刻被拦。这给了我一个重要线索这个WAF的检测点很可能集中在“关键字特征”而不是完整的SQL语义链。1.3 值不值得绕投入产出比的三个判断在正式尝试绕过之前我给自己设了三道判断题这也是我处理所有WAF绕过场景的习惯。第一目标在不在授权范围内。这个业务系统是某公司明确委托测评的对象本地镜像也是从对方提供的配置中还原出来的合法边界清晰可以继续深挖。如果是编外系统不管技术上有多少种绕过方式我一步都不会动。第二如果绕过了能拿到什么。通过接口的报错回显我判断这个注入点很可能支持联合查询注入后端数据库权限如果没有做最小化意味着可以读全库数据。业务影响面大值得投入时间。第三时间成本能不能控制。项目周期只有一周WAF绕过这种不确定性问题不能无限耗。我给自己定了一条硬约束在本地环境最多测三天三天后无论结果如何都回到按既有防线验收并输出加固建议的轨道上。三关都过了才真正开始分析WAF的检测逻辑。2. WAF的检测机制正则、语义、行为三层网2.1 WAF到底在检查什么想绕过一个防御系统第一步永远是摸清它的工作方式。很多刚接触安全的朋友会把WAF想象成一个“遇到危险字符串就打回去的保安”这个理解太粗糙了。实际上的WAF更像“门禁、录像、行为分析”三种角色兼任的系统。它通常做三件事请求解析把HTTP请求拆开搞清楚URL、Header、Body、Cookie里各装了什么规则匹配把请求内容归一化后丢进特征规则集做匹配命中的直接拦截会话管理对来源IP、Cookie做频率控制和信誉判断短时间密集请求会触发封禁。其中决定“绕不绕得过去”的核心在第二步的“归一化规则匹配”。归一化的意思是把URL编码、Unicode编码、大小写、重复斜杠等还原成一个标准形态再交给规则去比对。这套设计初衷是防止攻击者用简单的编码变化绕过规则但“归一化”本身也带来很多可以钻的缝隙——因为WAF归一化所依据的规范跟后端Web应用实际解析请求时遵循的规范经常不是同一个东西。2.2 正则规则集的天然盲区绝大多数开源WAF的默认规则集还是以正则匹配为主。正则擅长抓那些“特征明显”的攻击比如SQL注入里常见的union、select、sleep、information_schema。这类规则的盲区也很典型。第一规则往往只匹配“连续的关键字”如果把关键字用注释符、换行、特殊空白拆开规则就抓不到了。第二有些正则写得不严谨混入大小写就可能逃过去年。第三如果规则引擎对匹配内容长度设了上限超长的垃圾数据填充就能让特征“滑出”检查窗口。这里要补充一个关键认知正则盲区不等于系统漏洞。WAF不是数据库防火墙它在设计上就没打算替代应用自身的参数过滤规则集做得再大也只是“尽可能覆盖已知攻击模式”。绕过一次正则不难但想让绕过能稳定复现就必须理解规则背后的设计假设。2.3 语义分析与行为模型的不足这几年许多商业WAF开始宣传“语义分析”用算法理解请求里的SQL片段是不是真的进入了一条可执行查询路径而不是单纯看有没有特征词。听起来高级落地时问题不少。语义引擎对嵌套子查询、大量动态拼接、编码层数多的请求漏检率并不低而且语义分析消耗性能线上为了保护吞吐量往往不会把检查深度拉到最大。行为模型这边主要是看请求频率和会话特征。它对“同一个IP高频爆破”有效但对“一次精心构造的变体请求”基本无能为力——毕竟只发一个请求频率上没有任何异常特征。理解这一点对我后面的思路很有帮助我不需要以量取胜我需要让“单个请求在规则引擎眼里既不命中特征也不像攻击行为”。3. 绕过思路一SQL语义的等价变形3.1 不换功能只换写法SQL语言有一个特点同一个查询语义你可以写出非常多不同的表达方式。select可以写成SeLeCt字符串可以翻译成十六进制或char()拼接运算符可以从换成或真值条件可以从11换成aa。理解了这一点绕过的核心思路就清楚了——不去构造全新的攻击而是把原本会被规则命中的那个请求改写成语义完全等价、但形态上不含任何被收录特征的新请求。这个过程有点像同义词替换把“睡觉”换成“就寝”意思不变但关键词匹配就抓不到了。我第一步尝试的就是把特征集中在关键字上的规则。把union select改成union/**/select——中间插入注释块MySQL可以正常执行不少正则却因为要求“空格或边界字符”而匹配失败。这里需要说明如果WAF用的是简单的字符串包含检查这种改法也会被拦实际效果完全取决于规则写法。我在本地连续验证了十多种分隔符注释块、换行、制表符、%0a都会带来不同结果。3.2 运算符、函数与条件表达式的替换特征关键字不只是SQL关键字还包含函数名和常量。规则集里常见匹配对象是sleep()、benchmark()、if()、substring()这类函数。对函数名的替换我的经验是分两条路走。第一条路是函数名等值替换。substring可以换成mid或substrconcat可以换成concat_wssleep在MySQL里可以换成对一个字符串重复做计算密集操作的函数效果是产生延迟但特征名完全不同。当然第二种替换本身会引入很大的计算压力我在本地测试时只能用很小的参数防止把测试容器的CPU打满。第二条路是逻辑等价改写。比如布尔注入里经典的真条件if(11, 查全部, 查不出来)可以改成case when 11 then 查全部 else 查不出来 end。case when的写法不像SQL注入的标准形态因此能逃过很多规则集的函数黑名单。理解这个技巧比背模板有用得多因为规则集是活的今天能用的函数名明天可能就会被加进黑名单但“用等价语法表达同一逻辑”的思路基本不会失效。3.3 注释符、大小写与MySQL内联注释这里有一个值得单独拆开的MySQL特性内联注释/*!...*/。MySQL会把/*!开头的内容当作注释中的“可执行代码”处理也就是说/*!union*/在MySQL查询里会被解析成union关键字。但对WAF而言注释块一般会被归一化逻辑删掉或忽略很多正则引擎看到/*!就把它当注释排除了于是检查点和执行点出现了严重的不一致。/*!50000union*/这种写法数字部分表示MySQL版本号普通WAF更不会深究。我在实测中确实看到多个规则集对带版本号的内联注释完全放行。这个特性拼的就是“WAF的归一化规则与MySQL的真实语法规则不一致”。类似的语法差异还有很多#注释、--注释后面是否必须跟空格、;分号后堆叠查询的支持程度等等每一个差异都可能变成一个绕过窗口。我在测试记录里专门建了一张表把每个变体在不同规则集下的拦截情况、放行情况都记下来方便回溯。4. 绕过思路二让WAF“看错”请求4.1 编码层的多层解码差异等价变形是在语义层做文章编码技巧则是在解析层做文章。WAF要检查请求必须先解码应用要处理请求也要解码但两者的解码次数和顺序不一定相同。最常见的例子是双重URL编码。正常情况下单引号被编码成%27WAF解一次码就能还原成单引号并命中规则。但如果后端框架经历过两次解码真正到达SQL语句的其实是%2527——WAF只解一次的话它看到的是%27这个字符串而不是单引号规则自然就放行了。这种方法的成败取决于后端技术栈的解析层数。我在本地试过不同框架的靶场环境有的框架确实只解一层双重编码会导致数据本身出错根本不进入SQL上下文但也确实有版本会解两层这时WAF就成了“瞎子”。所以我不建议把“双重编码一定能绕过”写死在自动化工具里正确做法是先用无害探针确认后端到底解几层再针对性构造。另外Unicode兼容字符、畸形Content-Type编码声明组合也值得测但成功率更低且依赖具体后端组件的实现。这类技巧我只当作补充手段不指望它打主力。4.2 协议与参数处理的解析差异第二类“看错”方式发生在协议层。HTTP协议本身允许同名参数出现多次比如?id1id2。不同Web容器对同名参数的处理策略不一样有的取第一个有的取最后一个有的拼成逗号列表。WAF在检查时经常固定取第一个或最后一个但后端取的值如果跟WAF取的不一致就形成了参数污染的空间。更常用的场景是WAF把请求按application/x-www-form-urlencoded解析但攻击者把Content-Type改成multipart/form-data并在multipart某个字段里塞进攻击载荷。有的WAF对multipart的解析深度有限尤其当一部分字段被当作文件流时检查可能中断而后端框架却是按默认方式解析全部字段的。协议层技巧最大的问题是“脆”。有效性强烈依赖具体WAF解析器版本版本一升级绕过就失效。而且这类请求在运维眼里看起来就“不像正规请求”容易引起告警和人工复核。我自己的原则是这类手段在本地做验证可以不到关键阶段绝不在线上轻易使用。4.3 垃圾数据干扰可用但脆弱垃圾数据填充的思路是用大量无害字符把请求上下文膨胀到超过规则引擎的处理能力让特征被“淹没”。常见做法是在Payload前后加很长串的空格、无意义字母、无意义参数或者用多个超长Header字段挤压检查资源。我在本地测过效果确实存在尤其当规则引擎对单次匹配长度设了上限时作用很明显。但问题同样明显第一日志里会产生明显异常记录非常容易引起安全运营人员的注意第二请求体积变大之后Web容器自身的参数大小限制会先触发反而暴露了测试行为。所以这类技巧在我这里排名靠后属于“迫不得已才上”的一类。5. 本地复现与实测哪些技巧属于“纸面功夫”5.1 最小实验环境怎么搭绕过技巧不能光看文档必须有一个可以反复折腾的环境。这次我用的是一台本地测试虚拟机上面起了两个容器一个跑存在注入问题的简易Web应用另一个跑开箱即用的开源WAF容器。网络拓扑很简单请求从抓包工具发出来先打到WAF容器再由WAF转发给后端的Web应用容器。选这个拓扑的目的是让“被拦/放行”完全由WAF规则决定不混进云服务商自带的安全策略。商用云WAF的规则集不透明出了绕过也说不清踩的是哪条规则回归复现很麻烦。开源方案的优势是所有日志、规则ID、命中详情都是明文排查起来非常舒服。具体步骤上先把WAF容器配置成反向代理模式监听本机端口再把Web应用容器配置在内部网络最后在抓包工具里把请求的Host指向WAF端口。整个过程大约半小时。如果只想快速验证一个思路不需要完整业务数据用教学用的靶场应用就够了。5.2 用日志和自动化脚本做回归有了环境我开始批量验证。手工测几十个变体没问题但我想观察“规则集对某一类变体的整体放行率”所以写了一个简单的自动化脚本准备一组基线样本包括原始注入Payload和改造后的变体循环发送请求每次记录返回状态码和WAF日志里的规则命中结果最后汇总成一张表。有了日志判断一个请求是否被拦截就不需要靠猜。日志会明确写出命中的规则ID、请求方向、归一化后的数据有些WAF甚至会输出判定理由。这一步对后面写报告至关重要——每个绕过的变体都能对应到具体规则盲区而不是笼统地说“WAF可绕过”。5.3 三类技法的实测数据与选择建议我把三天里的实测结果按三条思路汇总了一下绕过思路在默认规则集下的表现可靠性SQL语义等价变形效果最好覆盖面广中高规则升级后需重新验证编码层差异只在特定后端框架上有效中低依赖解码链协议与参数处理差异能绕过部分请求但极不稳定低版本升级即失效垃圾数据填充偶尔有效极易触发告警低不推荐线上使用实测也给了我一个反直觉的经验很多人以为绕过最依赖“多背几个Payload模板”其实真正拉开差距的是对“WAF解析结果与后端解析结果不一致”这个差异点的理解。一个冷门的编码差异可能比一百个变形的union select都值钱。6. 反推防守每一类绕过都对应一条加固项6.1 别把WAF当救命稻草这个项目做完我在测试报告第一页就写了一句很直接的话WAF绕过能被验证出来最根本的原因不是WAF规则写得差而是应用程序自己把不可信输入直接拼进了SQL语句。WAF本质是一道外围防线它可以拦住大多数批量漏洞扫描但面对一个清楚自身技术栈的攻击者天然存在“解析差异”这个结构性问题。所以防守方首先要转一个弯不要问“WAF能不能挡住所有攻击”而要问“即便WAF被绕过了我们应用的SQL还有没有注入的可能”。答案如果是否定的那WAF有没有被绕都无所谓答案如果是肯定的那不管WAF升级多少次风险都不会真正消失。6.2 应用层加固的优先级排序基于这次绕过的经验我给那个业务系统按优先级排了整改项第一所有SQL改成参数化查询或ORM绑定。这是唯一能从根上消除SQL注入的做法。老实说如果一个模块已经彻底参数化我在做测试时根本不会走到WAF绕过这一步因为注入点根本不存在。第二输入校验做白名单。像report_id这种参数如果业务上只接受正整数直接在入口处用严格正则校验非数字直接拒绝。白名单比黑名单可靠得多因为白名单天然不受“新攻击变体”影响。第三数据库账号做最小权限。我在测试中能拿到全库数据很大程度是因为应用连库的账号权限过高。如果应用只读自己需要的那几张表即使注入点存在影响面也会被大幅压缩。这三条做完WAF的升级压力其实就小了很多它的任务只剩拦住学生式扫描器。6.3 WAF规则升级与回归测试的常态化最后是WAF侧的改进。我给对方的建议不是简单地说“升级规则”而是建立一套回归测试机制。具体做法是把我测试中用到的所有绕过样本、对应的URL、参数、预期结果保存成脚本库每次WAF规则有更新运维团队把这个脚本库跑一遍对比哪些被拦了、哪些被放行了。这个机制的价值在于让“攻防变化”变得可见。安全领域的攻防是动态的今天有效的手法和防御明天可能都会失效。如果没有一套可回归的样本库你根本不知道规则更新到底是变好了还是变差了有了它规则变更是否引入新的绕过窗口五分钟就能得出结论。写到这里想补一句这次项目的真实体会绕过一次WAF并不难真正难的是把绕过经验变成有效的安全建设输入。测试报告如果只写“存在WAF绕过风险”五个字负责修复的人多半无从下手但如果你把每一步请求、日志命中详情、绕过后的实际影响、每一类绕过对应的加固项都写清楚这个漏洞就变成了一次很好的加固契机。最后分享一个我一直保留的工作习惯每次做完这类测试我都会把新验证成功的绕过样本存成脚本放进本地回归集里。下一次跑同类项目或者产品规则升级后需要复测时五分钟就能知道WAF有没有退步。这套习惯比任何单一工具都更能建立安全感。
RELATED

相关推荐

在线 IP 地址解析 API 实战:地理位置、AS 号与域名反查

在线 IP 地址解析 API 实战:地理位置、AS 号与域名反查

做后端服务的这几年,我发现在很多业务场景里,IP 不仅仅是一个网络层地址,它其实是一张“身份名片”。比如你现在接入一个请求,拿到用户 IP 后,最本能的需求是知道对方从哪里来、在哪个城市,稍微深度一点的&…

📅 2026/10/13 15:25:25
深入理解进程虚拟地址空间:从内存布局到MMU页表翻译

深入理解进程虚拟地址空间:从内存布局到MMU页表翻译

不少朋友学到进程这一章时,都会卡在“虚拟地址空间”这个名字上。进程不是一块正在运行的程序吗,哪来的“空间”?你写C语言时打印出来的指针地址,到底是不是内存条上的真实位置?为什么父进程和子进程里同一个全局变量&…

📅 2026/10/13 15:25:25
Word公式粘贴到网页编辑器:从OMML到LaTeX的完整转换链路

Word公式粘贴到网页编辑器:从OMML到LaTeX的完整转换链路

教育信息化推进到今天,公式输入仍然是最容易让老师和学生都崩溃的一环。我们这边使用的XHEDITOR在线作业系统,学生交作业的入口是一个基于contenteditable的富文本编辑器,数学、物理、化学老师又都是Word的老用户,“我在Word里排好…

📅 2026/10/13 15:25:25
MORE NEWS

更多资讯

📰

GOCI2波段手册全解读:从波段表到月球辐射定标与在轨监测

简介:GOCI2说明手册聚焦第二代静止轨道海洋水色成像仪波段设计,面向海洋遥感科研人员、卫星数据处理工程师及相关专业学生。系统梳理了传感器波段设置、辐射性能指标与硬件组成,助读者理解各通道中心波长、带宽、标称辐射亮度、最大海洋辐射亮…

📰

OpenClaw本地接入QQ Bot全流程:从零配置到消息互通

我自己在本地折腾 OpenClaw 接 QQ Bot 的时候,说实话踩了不少坑。网上能搜到的资料大多是“配置成功”的结果展示,真正把从零到打通这条链路讲透的很少。这篇博文就围绕 OpenClaw 本地配置 QQ Bot 的完整流程展开,把每一步背后的逻辑、容易翻…

📰

JavaEE酒店管理系统源码解析:从房态订单联动到毕设部署避坑

简介:这份资源是面向计算机专业毕业生与JavaEE初学者的小型酒店管理系统完整项目,基于经典SSH框架组合开发,适合用作毕业设计参考或企业级Web开发入门实践。项目模拟真实酒店业务流程,涵盖用户管理、房间状态与价格设置、预订入住…

📰

React Native鸿蒙跨平台 Badge 徽章组件动态样式封装实践

做移动端基础组件的时候,最容易被人忽略的就是 Badge 这种“小东西”。红点、数字角标、提示标签,几乎每个 App 都躲不开它,但真要动手封装一个经得起多端验证的徽章组件,才发现里面全是细节。这篇文章就聊聊我用 React Native 在…

📰

仓库架构风格:数据一致性与组件解耦的架构实践

我见过太多团队在架构选型时只盯着微服务和消息队列,一提仓库架构风格就觉得是"老古董"或者"太简单"。但说实话,仓库架构风格是软件系统里最常被低估、却又最能扛住业务演进的一种组织方式。它把组件之间的交互从"互相调用&quo…

📰

Python机器学习预测分析核心算法源码拆解:弹性网、随机森林与GBM实战

简介:这份资源面向具备一定Python基础、希望系统掌握预测建模的学习者与数据从业者,围绕Python核心预测算法展开,从线性回归、逻辑回归、决策树与随机森林,到支持向量机、神经网络、时间序列分析、梯度提升机、K近邻、朴素贝叶斯及…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬