WAF拦不住SQL注入?实战经验拆解绕过原理与防御策略
WAF 拦不住关于 SQL 注入绕过我聊点实际踩坑经验先说一个我自己的经历。之前在某项目做授权测试目标站点的 WAF 规则配得相当严手头一个注入点把把被拦报错、联合、布尔盲注全试了一遍WAF 直接给 403。我一度怀疑是不是注入点判断错了后来静下心把 WAF 的拦截日志调出来看才发现问题根本不是注入点选得不对而是我的请求在 WAF 眼里“特征太明显”。从那之后我开始系统梳理 SQL 注入和 WAF 绕过的关系也慢慢明白了一个道理WAF 拦不住 SQL 注入绝大多数时候不是 WAF 本身垃圾而是我们把“绕过”想成了魔法没把它当成逻辑博弈。这篇就和你聊聊我理解的 SQL 注入绕过原理以及我在授权测试里实际用过的分析思路。内容不吹不黑尽量说人话适合想搞懂 WAF 为什么拦不住、以及怎么从攻防两侧看问题的人。1. 先搞清楚 WAF 到底在拦什么很多人对 WAF 的理解就是“网站前面的一道墙专门挡黑客”。这话没错但太粗了。想搞懂绕过你得先知道 WAF 用什么眼光看流量它为什么能拦住一些注入又为什么会在某些场景下失手。1.1 WAF 的检测链路WAF 的检测过程一般分为四步数据采集、协议解析、规则匹配、动作响应。数据采集是说 WAF 先拿到原始 HTTP 请求包括 URL、请求头、POST 体、Cookie 这些内容。采集完之后做协议解析这一步很关键WAF 要把请求参数从协议里“抠”出来搞清楚哪些是参数键、哪些是参数值。然后规则匹配就是把解析后的内容丢进检测引擎看有没有命中已知攻击特征。最后动作响应按照管理员配置的策略执行放行、拦截、加验证码、记录日志等等。这里有个很多人没意识到的问题WAF 的检测效果高度依赖协议解析的准确性。如果 WAF 没能在协议解析阶段完整还原业务侧真正拿到的参数内容那后面规则匹配就像在一个半盲的人面前玩猜物——你猜得再准前提也得是你“看见”的东西是完整的。1.2 为什么“能拦到”不等于“拦得住”我见过不少安全从业者评价 WAF 时喜欢看“拦截率”好像拦得多就厉害。但拦截率高不等于安全其中一个原因是WAF 可能跑在“宁可错杀一千”的模式大量正常请求也被拦截业务方怨声载道最后管理员悄悄调低防护等级反而让真实攻击变容易。另一个原因更根本——WAF 的规则库是滞后于攻击手法的。WAF 厂商会基于已知的攻击样本提炼规则比如检测union select、sleep()这些关键词。但攻击者也在不断研究新花样只要找到某种方式让最终到数据库执行的语句“语义没变、但字面变了”WAF 规则就抓瞎。这里埋个伏笔后面讲的绕过原理基本都围绕“字面变了语义没变”这句话展开。我自己的体会是判断一个 WAF 好不好不光要看它能拦截哪些已知攻击更要看它在面对变体时会不会漏。一个真正成熟的 WAF规则之外往往还有语义分析、行为分析这类兜底机制。但现实是很多站点上部署的 WAF规则解析做得粗糙业务侧代码对参数的处理又五花八门这就给了绕过生存空间。2. 绕过背后的基础原理不是花招是逻辑缝隙聊到绕过很多人第一反应是“背 payload”。我见过一些新手拿着一张绕过速查表这条不行换那条试半天也不知道为什么成为什么不成。这路子走不远。绕过的本质是利用 WAF 识别结果与实际执行结果之间的不一致让 WAF 看到的和数据库最终执行的“不是同一个东西”。2.1 一切绕过的起点识别与还原的不对称数据库执行 SQL 之前应用程序代码会先对参数做各种处理比如拼进 SQL 语句、走框架的 ORM、做字符转换。WAF 的检测则是基于它自己解析出来的那份请求数据再模拟还原出一个它认为的“最终语句”。问题来了WAF 对最终语句的还原和真实业务代码对参数的还原往往存在偏差。举几个常见的偏差来源业务代码对参数做了 WAF 没预料到的解码比如先做 URL 解码再做 SQL 拼接数据库本身对某些字符序列有额外解析比如注释符优先级、换行符处理、字符集转换应用的中间层对特殊字符做了清洗、合并、截断导致 WAF 看到的参数值和实际进入 SQL 的值不一致。只要存在任何一种不对称就可能被尝试性地构造出“WAF 觉得安全、数据库觉得危险”的请求。说句大白话绕过不是去骗数据库而是去骗 WAF让它对请求的判断落空。2.2 编码与二次解码的博弈编码绕过是我个人觉得最容易理解的一类它完美体现“识别和还原不对称”的道理。比如一个 Web 应用接收参数后先做一次 URL 解码再拼进 SQL。WAF 在检测时如果只做了一层解码那么攻击者把关键字再编码一层WAF 还原出来的还是编码形态规则匹配自然命中不了。但数据库端执行的却是解码后的真面目攻击照样成立。这类问题的根源是解码层数不一致。我之前在测试一个接口时发现参数经过三层解码才最终被使用而部署的 WAF 只做了两层。那就存在可构造的编码链让注入特征藏到第三层里。当然不同编码方式组合起来变化更多URL 编码、十六进制、Unicode 编码都能叠加但核心思路是同一个。这里必须多说一句做这类测试仅限于自己有授权、目标是自己负责的业务系统。没有授权对任何系统做绕过尝试都不仅是道德问题而是实打实的法律问题。我这篇写原理和思路不是为了教人去破坏而是希望搞防御的人更懂攻击者的思维——你连攻击者怎么想都不知道怎么防得明白2.3 红队视角的绕过思路分类在测试项目里我习惯把绕过思路分成几类方便针对不同 WAF 类型快速尝试特征绕过让危险关键词不再以“原始形态”出现在请求里比如等价函数替换、注释拆分、字符串拼接。解析绕过利用 WAF 与业务侧对请求解析的差异比如参数污染、畸形请求、分块传输。逻辑绕过针对 WAF 规则本身的匹配逻辑漏洞比如空字节、大小写混合、超长数据截断。资源耗尽绕过让 WAF 来不及完成完整检测就放行这属于性能对抗前提是目标系统能承受对应的资源消耗风险更高我不建议在实际业务系统上轻易试。每种思路背后都有对应的原理也各有局限。不是说你背会了几种就能通杀一切 WAF真实测试里经常是几种思路混合着用。提前把思路框架理清楚比背一百条 payload 有用得多。3. 实战拆解从被拦住到穿过 WAF 的完整过程实战不是魔术它是一步步排除变量、逼近目标的过程。这里我把自己的分析流程拆开给你看场景做了脱敏但思路保持原样。整个过程不涉及具体目标的利用细节重点放在你要怎么思考、怎么验证。3.1 场景复现与目标分析假设你拿到授权的一个测试目标业务系统是一个常规的 Web 应用从 URL 参数、POST 表单到 JSON 接口都有部署了某知名商业 WAF。你的任务是在授权范围内评估注入风险而不是真的去把数据拖出来。首要目标就是找到一个注入点并判断它到底能不能被利用。选择的注入点尽量不要选那种“所有条件都对你有利”的点否则后面得不出普遍结论。我当时找的是一个带有数字参数的查询接口接口逻辑比较直接适合分析绕过过程。拿到注入点后先做一个基础判断参数是字符型还是数字型闭合符是什么正常响应和异常响应差在哪这一步决定了后续构造什么形态的语句。不少新手上来就拿通用 payload 一顿乱打连闭合方式都没搞清打进去全是语法错误反而很难判断 WAF 是在拦你还是数据库在报错。3.2 分析拦截日志确定被什么规则命中这一步很关键。WAF 拦你是有理由的你要看它到底在哪个环节、哪条规则上拦住你。很多 WAF 的管理后台能看到拦截原因比如命中了哪个规则 ID、匹配了哪个关键字、拦截的是 URL 还是 POST body。我当时做了个非常“笨”但有效的操作把每次被拦的请求和对应的拦截日志保存下来做了一份清单。结果发现第一类请求被拦是因为出现了union select的字样命中了某种关键字规则第二类请求被拦是因为请求头里有异常 UA命中了某种人机识别规则第三类请求被拦是因为 POST body 长度超大并包含可疑片段被模型判定为高风险。看到没不同的拦截原因对应不同的检测机制。如果你不看日志光知道“被拦”就盲目换 payload那纯属碰运气。看了日志你就知道要绕的是关键字规则还是绕人机校验还是控制报文形态。看日志是绕过测试标准的起手式但前提是你有权限访问这些日志。3.3 构造变体并验证有效性的方法论在了解拦截原因后接下来构造变体的方法论我总结成三步第一步保持语义不变改变字面形态。比如union select要做的是什么是联合查询。那就想办法让 WAF 认不出“union select”连在一起的样子但数据库仍能正确识别。常见思路包括插入注释符、利用等价写法、调整大小写。但注意注释符不是万能的某些 WAF 会先剥离注释再检测大小写只对区分大小写的检测规则有效对正则表达式的“忽略大小写”模式没用。第二步每次只改变一个变量。测试最忌一次改多个条件改完你就分不清是因为哪个变化生效。我当时保持 payload 语义不变只把某一处的写法替换成变体请求一次看 WAF 是否还拦。逐个变量试下来很快能定位哪种变形对这个 WAF 有效。第三步用响应差异做细粒度观察。绕过成功不意味着就能直接拿到结果响应可能还是错的。你得区分是 WAF 放行了但数据库语法错误还是 WAF 已经放行、数据库也执行了但数据没有回显。如果是后者说明绕过是有效的再接下去就要考虑回显路径的问题。我当时用时间盲注场景做验证在响应时间上做统计阈值得拉高多测几轮取平均值才能从网络抖动中把真正的时间差剥出来。这套方法论其实不复杂但很考验细心。我见过不少人输在“太急”上改了五个变量一起测WAF 没拦就以为自己绕过了其实可能只是这次请求没触发规则换个参数又拦住了。4. 常见问题与排查技巧实录实战做多了总会遇到一些反复出现的坑。整理成文字给后来人省点时间。4.1 WAF 规则误报与漏报的典型场景误报的意思是正常请求被拦漏报就是恶意请求被放行。这两种情况都会让人挠头。误报常见于参数内容包含敏感词的场景。比如搜索“drop table”的课堂名或者查询“select * from courses”这种教学字符串业务参数本身没恶意但字面上触发了规则。处理方式不是去跟 WAF 硬刚而是先确认触发原因再走白名单或调整规则。漏报则往往藏在非常规参数位置。WAF 检测一般主要盯 URL、POST body、Cookie但 HTTP 头里自定义字段如果被业务代码拼进了 SQL而 WAF 又没对这个字段做完整检测就可能漏。这不是 WAF“不行”而是部署配置时业务侧没有把全部参数来源告诉 WAF。经验之谈排查 WAF 漏报先把业务代码里所有涉及 SQL 拼接的参数来源都列出来再来对照 WAF 实际检测的参数范围。很多漏洞不是 WAF 无能而是“业务侧出参渠道太多、WAF 管不过来”。4.2 验证绕过有效性的三种方式测试时怎么判断这次变形到底有没有绕过成功我常用的三种方式直接看响应状态WAF 拦截通常伴随 403、406 或自定义拦截页如果状态码回到正常范围至少说明 WAF 这关过了。对比基准请求拿一个完全合法但结构类似的请求做基准观察响应差异。如果变体请求和基准请求表现一致说明你的变体通过了 WAF 初筛。数据库行为特征分析走时间盲注或报错盲注逻辑确认数据库层面有没有产生预期行为差异。这一步最关键因为 WAF 放行只是“过门”数据库执行才是“进门”。4.3 工具使用的边界与注意很多自动化测试工具内置了绕过 payload 库能极大地提高测试效率但用起来有几个要注意的边界。一是工具的 payload 库更新速度不一定跟得上 WAF 规则演进而且工具通常是“广撒网”式测试产生的告警日志量很大容易惊动防守方。二是工具对目标业务的理解能力几乎为零很多业务独有的参数处理逻辑工具完全覆盖不到这时候手动分析的价值反而更大。我在测试中一般是先用工具做信息收集和方向探测到关键注入点的绕过环节更多依赖手动构造请求和脚本辅助。工具本身无善恶关键是使用的人有没有授权。任何绕过测试都应当在目标系统所有者明确授权的范围内进行。这点再怎么强调都不为过。5. 从防御侧看如何让 SQL 注入“无路可绕”讲了半天攻击侧思路但说句实在话我在实际项目里最想看到的不是“谁能绕过 WAF”而是“业务系统能不能做到让 SQL 注入无路可绕”。防御不是靠堆一台 WAF 就完事而是多层配合。5.1 根因治理参数化查询是底线SQL 注入的根本原因是把数据和代码混在了一起。只要业务代码用字符串拼接的方式构造 SQL不管前面挡了多少层 WAF注入的风险就永远在绕过只是时间和技巧问题。解决根因的方法就是参数化查询让 SQL 语句的结构和参数分离。数据库先编译 SQL 结构再传入参数值参数永远被当作数据对待从机制上杜绝了注入。现在主流编程语言的数据库驱动都支持参数化几乎没有技术门槛。我在评估一个系统的安全性时第一件事就是看它数据库访问层有没有全量使用参数化。如果用了WAF 就算偶尔漏报注入这条路也基本被堵死。5.2 WAF 配置调优的原则如果业务侧暂时没法快速改造代码那 WAF 配置调优就是必须做的功课。我总结几条原则检测范围要覆盖全不只是 URL 参数POST body、Cookie、HTTP 头里的自定义字段都要纳入检测否则等于留了侧门。规则模式要和业务场景对齐在保证安全性的前提下减少误报误报多了必然导致运维人员“狼来了”疲劳最后手动静默关规则比不装 WAF 还危险。日志和监控闭环拦截日志要有持续关注机制不能拦住就完事还要定期分析攻击趋势反哺规则优化。5.3 纵深防御怎么落地WAF 只是纵深防御里的一层不是全部。我的经验是安全建设优先考虑三层代码层参数化查询、输入校验、输出编码把漏洞扼杀在源头。运行层数据库账号最小权限、Web 中间件加固、出网 ACL 限制把单点漏洞的影响范围压到最小。检测层WAF、RASP、主机审计、日志分析多层检测联动提升攻击者被发现的概率。这套组合下来即使某一层被绕过攻击者也会在下一层遇到更大的阻力整体安全水位比单靠一台 WAF 高得多。我在实际项目中见过太多“以为装了 WAF 就万事大吉”的团队等到被穿透了才开始补代码。安全没有银弹攻防对抗的本质是持续的认知博弈。希望这篇文章能帮你少走点弯路——无论你是做防御的还是做测试的都先把原理吃透再把工具和流程用对比什么都强。最后再分享一个小心得我在做注入测试的时候习惯在每一轮请求里记录下“WAF 是否拦截 响应差值 尝试的变体类型”三列数据。等测试结束再回头翻会发现很多当时没注意的规律比如某个 WAF 对注释符特别敏感、对某种编码绕过完全无感。这些规律攒多了就成了你自己的绕过画像库下次遇到同类设备起步就能快很多。当然这一切的前提依然是你做的每一行测试都在授权范围之内。

相关新闻

AI编程助手行为审计:Gryph本地监控实践

AI编程助手行为审计:Gryph本地监控实践

Gryph最初是我们团队内部为了解决一个具体到让人频繁失眠的问题而动手写的:AI编程助手已经进入日常开发流程的骨髓,可是我们发现,没有人能清晰地回答“我的代码到底被发到哪里去了”“它读过的本地文件里有没有不该被读取的东西”“如果它哪天…

2026/10/19 11:57:11 阅读更多 →
DeepSeek Infra仙人:从MoE到GRPO的全栈优化拆解

DeepSeek Infra仙人:从MoE到GRPO的全栈优化拆解

如果你这两年一直泡在 AI 社区,八成见过这个说法:DeepSeek 是 Infra 仙人。第一次听到我也觉得夸张,但把他们的技术路线从头到尾翻完,我反而觉得这个词挺精准——“仙人”不是玄学,而是把别人不愿意抠的系统级细节一个…

2026/10/19 11:57:11 阅读更多 →
大学生必学的c51点灯

大学生必学的c51点灯

补充一个知识点 这个像不像一位八段数码管&#xff0c;会点灯就等等价于会静态数码管显示了。 1.流水灯 方法一&#xff1a; #include <REGX52.H> //也可以使用<REG52.H> 缺点是只能对P2整体操作&#xff0c;不能操作单独操作一位&#xff0c;如P2_0 #include &l…

2026/10/19 11:56:09 阅读更多 →

最新新闻

Product Hunt热榜复盘:从票数与评论区看产品冲榜策略

Product Hunt热榜复盘:从票数与评论区看产品冲榜策略

我每天早上都会打开 Product Hunt 的热榜页面&#xff0c;这已经是雷打不动的习惯。2026-03-31 的这份榜单&#xff0c;我点开之前其实没抱太大期待——毕竟“每日热榜”四个字听起来就像流水账。但真正逐条把当天上榜的产品翻完、把评论区看完、把背后几位创始人的发布策略捋清…

2026/10/19 18:05:50 阅读更多 →
C#类成员详解:字段、属性、构造函数及面试要点

C#类成员详解:字段、属性、构造函数及面试要点

1. 为什么"简述类成员"值得你认真准备 我以前参与技术面试时&#xff0c;经常用"简述类的成员"这道题做开场。原因很简单&#xff1a;它听起来基础得不像话&#xff0c;但实际考察的层次非常深。一个候选人如果连这道题都答得有结构、有主次&#xff0c;那…

2026/10/19 18:05:50 阅读更多 →
达梦8 DSC共享存储集群在VMware虚拟化环境中的部署实践与踩坑指南

达梦8 DSC共享存储集群在VMware虚拟化环境中的部署实践与踩坑指南

1. 项目拆解&#xff1a;达梦8 DSC在虚拟化环境中的定位和难点1.1 达梦8 DSC到底是什么达梦8的DSC全称是Data Shared Cluster&#xff0c;也就是共享存储集群方案。它的逻辑跟很多人熟悉的Oracle RAC类似&#xff1a;两个或多个实例节点&#xff0c;通过共享存储访问同一份数据…

2026/10/19 18:05:50 阅读更多 →
Python全栈从0到上岗:项目实战打通全链路

Python全栈从0到上岗:项目实战打通全链路

先泼一盆冷水&#xff1a;全网铺天盖地的“Python全栈教程”“30天从入门到精通”“学完就能月薪过万”……至少有一半是坑。不是蹭这个项目标题的热度&#xff0c;是我见过太多零基础学习者&#xff0c;对着几百集的视频刷了三个月&#xff0c;笔记记了好几本&#xff0c;但打…

2026/10/19 18:05:50 阅读更多 →
Java连接MySQL全流程详解:JDBC原理、连接池、事务与性能优化

Java连接MySQL全流程详解:JDBC原理、连接池、事务与性能优化

只要你的 Java 项目叫“业务系统”&#xff0c;就逃不开 MySQL。这句话我说了快十年&#xff0c;带过的新人没有一个能反驳。很多人第一次连数据库&#xff0c;都是在搜索引擎里复制一段三行代码&#xff0c;跑通了就丢到生产环境&#xff0c;然后被时区报错、中文乱码、连接超…

2026/10/19 18:05:50 阅读更多 →
从零实现轻量级图表编辑器:Canvas渲染与数据驱动架构解析

从零实现轻量级图表编辑器:Canvas渲染与数据驱动架构解析

图表设计工具这类项目&#xff0c;名字叫 "diagram-design" 的其实蛮多的&#xff0c;但真正能让人愿意截图发出来的产品很少。我这次想聊的&#xff0c;是我自己从零搭起来的一个轻量级图表设计方案——一个跑在浏览器里的流程图、架构图编辑器。核心思路很简单&…

2026/10/19 18:04:45 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介&#xff1a;本资源为电源技术方向的学术研究文档&#xff0c;聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略&#xff0c;面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址&#xff1a; https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集&#xff0c;共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →