Sqllibs Less 23-28:SQL注入过滤绕过实战复盘
刚把 Sqllibs 靶场里的 Less 23 到 Less 28 这一组关卡跨过去感觉特别值得写一篇复盘。前面二十几关教的是基础注入联合查询、报错注入、盲注大家练得再熟也只是在同一种固定套路里反复确认“这里能注入、那里也能注入”。但到了 Less 23 以后难度突然换了一个方向注释符被过滤、OR 和 AND 被打回、空格被吞掉、连括号都要自己数。这说明注入开始从“找漏洞”变成“绕过防御”更接近真实环境里碰到的场景。这篇文章不打算把所有 payload 机械地背一遍而是想带着你把每一关背后的过滤逻辑理清楚为什么这个写法能绕过绕过之后 SQL 最终变成了什么适合刚学完基础注入、准备进阶过滤绕过的同学也适合做开发的同学看一眼——你们真的以为过滤掉几个危险字符就安全了吗还早得很。1. Less 23-28 的系统设计一张表看清六道关卡的递进关系1.1 从基础注入到过滤绕过的分水岭Sqllibs 中前面几关更像“漏斗”测试先用单引号试错发现 SQL 报错再判断字段数然后 UNION 或盲注拿数据。这套流程一旦熟练很多关卡其实长得差不多。Less 23 开始加入了服务端过滤等于考试从“开卷考”变成了“闭卷考”不是不知道答案而是答案里的一部分字符被划掉了。这种设计非常贴近真实系统。现在很少有应用把输入原封不动拼进 SQL多数多少会加一点防护最常见的就是黑名单过滤。但黑名单的问题在于它依赖维护者把所有危险写法都列干净而这几乎不可能。Less 23 到 28 就是一组围绕黑名单的对照组过滤注释符、过滤 or、过滤 and、过滤空格、过滤关键字、再加上括号包裹。每关只变一个维度你就能清楚看到“过滤规则不同绕过思路也完全不同”。1.2 六关的能力要求速查表先给一张总表后面的小节再逐关展开。不同版本的 Sqllibs 具体关卡细节可能有点差异但技能点基本落在这些地方。关卡常见过滤内容核心训练点越接近实战的收获Less 23过滤--和#类注释符不用注释符也能闭合引号理解语句如何被补成一个合法整体Less 24入口点过滤不明显二次注入注入点不在第一个请求里Less 25过滤or和and双写绕过、逻辑符号替代判断黑名单是否只删一遍Less 26过滤空格、or、and、注释符无空格 SQL 构造应对\s类空白字符过滤Less 27过滤关键字但不够完整大小写混合黑名单不规范化时如何失效Less 28过滤空格、注释符、叠加括号多层括号闭合应对“查询外面套括号”的写法这张表想强调一件事Less 的核心不是“背一个 payload”而是“遇到一个过滤规则你能否马上判断出它整个只做了一层并且能不能用等价符号替代”。2. Less 23没有注释符时怎么把查询语句补成一个合法整体2.1 先搞清楚源码里的拼接姿势Less 23 的经典配置是这样的参数被直接拼进单引号包裹的 SQL 语句里同时服务端对注释符做了过滤。你可以理解为后端代码长这样$id $_GET[id]; $id str_replace([--, #], , $id); $sql SELECT * FROM users WHERE id$id LIMIT 0,1;;前面练得熟的人上来第一反应是输入?id1 --。但在 Less 23 里--会先被删掉剩下1 语句后半截直接变成WHERE id1 LIMIT 0,1自然报错。#也一样会被过滤。问题就来了传统注入靠注释符吞掉 SQL 语句尾部的引号现在尾部引号没法吞但前置的引号已经被我们闭合了那就必须让整个语句自己成立。2.2 用闭合引号替代注释符不依赖尾部注释的 payload 构造Less 23 最核心的思路就是“用另一个单引号去配对而不是用注释把尾部引号废掉”。最经典的验证写法是?id1 AND 11拼出来是SELECT * FROM users WHERE id1 AND 11 LIMIT 0,1;注意我们输入最后那个补上了源码里的收尾引号这样整条 SQL 就是一个合法表达式前面1已经把查询条件闭合后面AND 11永远为真所以页面正常返回。联合注入的写法同样由这个思路变形而来?id-1 UNION SELECT 1,2,3 AND 11拼出来大约是SELECT * FROM users WHERE id-1 UNION SELECT 1,2,3 AND 11 LIMIT 0,1;这里用-1让前面的查询结果为空UNION 后面的部分来决定页面显示哪几列。AND 11不再负责“布尔判断”而是负责补引号同时还不会让整条语句失败。这就是 Less 23 和前面 Less 最大的区别你不需要注释符你需要的是让 SQL 的引号数量刚好匹配。2.3 我在这一关容易踩的坑第一个坑是 URL 编码问题。很多人在浏览器里输入#结果发现 URL 到#就断了因为#在 URL 里是锚点标识。必须写成%23。但在 Less 23 里即使写成%23后端解码后也会被过滤所以别在注释符上死磕。第二个坑是列数检测。平时习惯用order by 3但注释符被过滤后直接写1 order by 3会因为尾部引号没闭合而报错。可以改成1 order by 3, 11把最后一个单引号补上。不过说实话在已知是 union 注入的前提下直接用UNION SELECT 1,2,3试错比 order by 更快错了就换UNION SELECT 1,2,3,4直到不报错为止。第三个坑是盲目套用“注释符过滤”就以为不会注入。Less 23 恰恰在告诉你注释符本质上是 SQL 语法的一部分但 SQL 语言本身有非常强的自我闭合能力。只要你能把条件表达式补完整尾部注释根本无所谓。3. Less 24二次注入的完整利用链比普通注入更隐蔽3.1 注入点不在第一个请求里Less 24 是很典型的二次注入关卡。它的特点是注册用户时输入的内容不会立刻触发 SQL 注入而是在后面某个操作中重新进入 SQL 语句。不少人在入口点试了半天发现普通注入没戏就直接跳过了。这其实漏掉了一个非常重要的场景危险数据会被存储之后被其他代码段不加处理地拼进 SQL。简单理解就是第一次写入数据库时如果代码用了转义或预处理单引号还能被安全存下来。但后续某个功能比如“修改密码”它把数据库里读出来的用户名直接拼到UPDATE语句里那么注入就可能在第二次请求时发生。3.2 一次模拟实验的完整时间线在本地靶场里可以按这个流程复现注册一个用户用户名写成admin#这个#会被原样存进数据库。用这个账号登录系统会把用户名带进会话。执行修改密码操作后端执行类似UPDATE users SET password$newpass WHERE username$username的语句。如果$username是admin#拼出来的 SQL 就是UPDATE users SET passwordhacked WHERE usernameadmin# AND passwordoldpass;由于#后面被注释掉这条语句真正生效的是WHERE usernameadmin。也就是说攻击者把自己账号的密码改成了任意值同时也把管理员账号的密码改成了这个值。整个过程里注册接口没有直接暴露注入因为它很可能用了预处理或转义。真正的问题出在“使用数据的时候”没有再次转义。这种“存储—再使用”的链路在真实业务里非常常见比如昵称、地址、备注字段很可能只在校验当前接口却忽略了后台管理页、通知消息、报表查询等后续环节。3.3 二次注入给开发的启示Less 24 最值得学习的其实不是怎么打而是怎么防。黑名单、转义函数、过滤这些手段都只能保护“当前调用点”。只要后面有代码从数据库读数据再拼 SQL前面再好的过滤都会失效。唯一的根治办法是全链路使用参数化查询存储过程、ORM、占位符都行关键原则只有一个SQL 结构固定数据永远是参数绝不能把外部或数据库读到的内容变成 SQL 代码的一部分。另外补充一个排查技巧怀疑存在二次注入时可以特意注册一个带引号的用户名然后去看“修改密码”或“个人信息更新”这些操作的数据库日志。一旦日志里出现异常 SQL 或报错就说明这条链路没有做参数化。4. Less 25、26过滤 or/and 和空格如何系统化绕过而不是背字典4.1 双写绕过 or 的核心条件Less 25 的主要过滤目标是or和and。很多初学者碰到1 or 11被过滤就麻了其实第一步是判断后端到底怎么过滤。如果它是“先删除一次”而不是“循环删除到没有为止”那么双写就能轻松过关。举个例子假设后端处理代码是$id str_replace([or, and], , $id);注意这个替换是一次性替换不是循环。当我们输入?id1 oorr 11过滤时oorr中间被删掉一个or剩下的内容恰好是or。原 SQL 变成SELECT * FROM users WHERE id1 or 11 LIMIT 0,1;这就实现了从oorr到or的“还原”。同理anandd会被还原成and。双写能不能成功取决于过滤逻辑是不是“一次删除”而不是“递归删除”。很多粗糙的 WAF 或自研防注入函数都是前者所以这种低成本的绕过方式直到今天仍然能碰到。另外如果过滤只针对关键字文字我们还可以用符号替代。在默认 MySQL 模式下||等价于or等价于and。比如?id1 || 11这种写法没有出现or和and照样能达到布尔逻辑效果。但要注意不同数据库对||的解释不一样Oracle 和 PostgreSQL 里它是字符串拼接MySQL 默认才当逻辑或。做题时先确认数据库类型再决定用不用符号替代。4.2 空格被过滤后的三板斧Less 26 在 Less 25 的基础上增加了“空格过滤”有些版本甚至会过滤注释符。空格的过滤一般来自类似preg_replace(/\s/, , $id)的正则意味着%20、%09、%0a这些常见替代空白也可能被吞掉。这时候需要三板斧依次尝试。第一板斧是换空白字符。URL 里的%09是制表符%0a是换行%0b、%0c、%0d也都是不同形式的空白。MySQL 解析器会把它们当成普通空白处理。如果后端只删了半角空格没有删制表符那么1 %09or %0911照样能过。第二板斧是彻底去掉空格改用括号和特殊语法重新组织表达式。比如select(1)括号替代了select和参数之间的空格from(users)替代了from和表名之间的空格where(11)同理。许多 SQL 函数调用都可以把参数直接放进括号不需要空格。第三板斧是用等价操作符和双写把敏感词规避掉。在 Less 26 那种“空格、or、and、注释符”全过滤的条件下可以构造一个完全不依赖空格和敏感词的报错注入例如?id1||(select(1))||11这里用||替代or用(select(1))消除空格末尾用||11闭合引号。整体没有空格也没有or、and、#、--但 SQL 的合法性和注入逻辑都保留了下来。这里要提醒一下一道题的本意不是让你死记某个%0b而是训练你“在字符级别想问题”。你可以先用 Burp 的 Repeater 逐个尝试空白字符变体再从后端响应差异里判断哪个字符被保留了。这个排查习惯比拿到一个万能 payload 重要得多。5. Less 27、28大小写混合与多层括号闭合的最后一公里5.1 为什么UnIoN SeLeCt能过而union select不能Less 27 训练的是一种非常矛盾但真实的防御漏洞黑名单不完整。假设后端过滤代码写的是$id preg_replace(/union|select/i, , $id);那UnIoN SeLeCt也会被过滤因为正则带了i修饰符大小写不敏感。但如果后端写的是$id str_replace([union, select], , $id);它只删小写组合那么输入UnIoN SeLeCt就不会被匹配到。SQL 关键字本身不区分大小写所以弱过滤很容易被混合大小写绕过。在 Less 27 里常见的验证方法是?id-1 UnIoN SeLeCt 1,2,3如果页面把 2、3 等数字回显出来就说明union select成功执行。这里的关键不是大小写本身有多神奇而是你必须判断后端的黑名单到底有没有做字符串规范化。如果它先用strtolower再过滤那大小写混合就完全没用如果它只是简单匹配那混合写法是成本最低的绕过方式。这个思路可以延伸到unionselect之间混入注释、换行、特殊空白字符。总之黑名单过滤只要没有经过“先统一转小写/转大写再去删”的步骤绕过空间就会大很多。5.2 括号闭合数括号比背 payload 重要Less 28 的最大看点是“括号”。有些 SQL 语句本身就在外面套一层括号比如SELECT * FROM users WHERE (id$id);如果我们只按普通单引号注入的思路操作闭合引号之后可能还会多出一个右括号整个语句还是非法的。这时候需要先判断源码里$id前面一共有几个左括号答一个就是WHERE (后面的那个。所以输入-1) OR (11就能把这个括号闭合掉SELECT * FROM users WHERE (id-1) OR (11);这里括号数目刚好匹配先是WHERE (被-1后的)闭合新的(11里的左括号又被语句原有的语法或额外右括号闭合。实际做题时你会碰到两层甚至更多层括号的情况那就得从内往外数。注入点前后到底有几个左括号没有闭合就补上几个右括号再通过一个合法的 OR/UNION 子句把控制权接回来。如果同时过滤了空格和注释符括号就是必须依赖的工具。比如? id-1)or((11结合 alternate whitespace maybe。但要记住括号闭合不是闹着玩的多一个少一个都会直接报错。我的排错办法是先输入一个观察报错样式再加一个)观察是否变化再加第二个)……每次只加一个符号响应从“报错”变成“正常”时就说明这一层闭合成功。这种逐字符逼近的方法比对着猜测的 payload 盲试靠谱得多。6. 防御视角和自动化测试从过关到真正能用的经验6.1 我刷完这些关卡后对“过滤”的新理解Less 23-28 这一整套刷下来最大的收获不是会了六个 payload而是知道了一个残酷的事实任何只靠黑名单字符过滤的防御都很难真正挡住懂语法的人。注释符不行就补引号or/and 不行就双写或用符号空格不行就换控制字符或括号大小写不行就编码绕过。攻击者不需要穷举所有可能只需要不断探测过滤规则里的漏网之鱼。关键短板有三处。第一黑名单覆盖面永远有限而且一次替换很容易被双写还原。第二过滤常常只发生在入口却忘了二次使用时同样不安全。第三有些系统对输入做了过滤却仍然用字符串拼接 SQL等于“把脏水滤了一遍然后倒进干净杯子”。真正有效的方案只有一个把数据当数据处理绝不让数据有机会被解析成 SQL 代码。6.2 在本地靶场里自动化工具要配合手工理解很多人一进靶场就开自动注入工具直接把整道题跑完其实少了很多乐趣也容易在真实环境里翻车。我建议的顺序是先在 Burp 的 Repeater 里手工判断注入点、闭合方式、过滤规则再让自动工具去跑剩余的数据提取。比如用自动工具扫描 Less 25-28可以指定一些通用 tamper 脚本但更重要的是理解它选了什么手法。如果自动工具选用“双写关键字”来绕or、用“hex 编码”来绕引号那不是魔法而是它把我们在手工环节验证过的思路固化了。对靶场来说最终结果不重要中间判断链最重要。另外练习归练习未经授权的系统绝对不要测。Sqllibs 这类本地靶场本来就是让你放开手脚试的一旦把 payload 打到不属于自己的线上环境那就不是学习而是违规违法的行为。6.3 给开发者的三句防注入忠告第一SQL 语句的结构必须固定所有外部输入一律用参数化查询或预处理语句绑定不要手动转义后拼接。第二数据库账号遵循最小权限原则应用连接账号不要给DROP、FILE、SUPER之类的高权限这样即使发生注入攻击者能做的事也有限。第三关闭页面上的详细报错输出把 SQL 错误记到后台日志里。很多人觉得报错注入是靶场才有的功能其实它能在真实系统里出现就是因为开发把数据库异常直接吐给了浏览器。我个人刷完这组题后的体会是Less 23-28 表面上是一组“绕过技巧”的练习题实际上是在训练一种“从语法层面理解 SQL”的能力。你一旦能不看 payload 就预判出过滤后的 SQL 长什么样才算真正过了这道坎。以后再遇到黑名单过滤第一反应就不是翻笔记而是问自己四个问题注释能不能用空格有哪些等价替代关键字能不能双写或符号化括号和引号到底需要补多少个这四个问题问完大多数不完善的过滤也就到头了。

相关新闻

文件上传校验真的防得住?实战盘点花式绕过与高频踩坑点

文件上传校验真的防得住?实战盘点花式绕过与高频踩坑点

文件上传校验真的防得住?实战盘点花式绕过与高频踩坑点 前言 文件上传漏洞是 Web 安全里最经典、出现频率最高的漏洞。很多新手以为上传防护只需要简单判断后缀名,写个黑名单就能搞定。但在真实渗透场景里,各种解析漏洞、后缀变形、条件竞争…

2026/10/15 6:48:50 阅读更多 →
TensorFlow深度学习工程化实战:从计算图原理到模型部署的完整链路

TensorFlow深度学习工程化实战:从计算图原理到模型部署的完整链路

1. 从“tensorflow”这个标题说起:它到底是什么,能解决什么问题如果你在技术社区里闲逛,看到有人只甩出一个词——“tensorflow”,然后底下跟了一堆讨论,你大概能猜到,这要么是个新手在问入门路径&#xff…

2026/10/15 6:47:49 阅读更多 →
iReport 3.0绿化版实战:轻量级报表设计器与JasperReports开发全解析

iReport 3.0绿化版实战:轻量级报表设计器与JasperReports开发全解析

简介:iReport 3.0完整绿化中文版是一套面向Java开发者的报表设计工具,特别适合配合JasperReports库快速创建、预览和生成报表模板。资源已做中文处理,解压后无需额外安装即可直接运行,共含191个文件,压缩包约48.81MB。…

2026/10/15 6:47:49 阅读更多 →

最新新闻

AIGC检测原理与降AI工具实测:论文从AI疑似到安全过关

AIGC检测原理与降AI工具实测:论文从AI疑似到安全过关

1. AIGC检测到底在查什么?先搞懂规则再动手说句实在话,2026年知网AIGC检测已经成为毕业论文、期刊投稿、课题申报绕不开的一道坎。很多同学拿着检测报告来找我,第一句话就是“我明明自己写的,怎么还标红一大片AI疑似?”…

2026/10/15 7:33:58 阅读更多 →
基于dartle的Flutter构建体系迁移到OpenHarmony的完整实践

基于dartle的Flutter构建体系迁移到OpenHarmony的完整实践

如果你也在做鸿蒙化适配,应该会遇到一个很尴尬的节点:Flutter代码好不容易在鸿蒙设备上跑起来了,但工程构建链路还停留在“脚本堆砌”阶段。各种.sh、.bat再加上 CI 平台上的自动化任务,项目一多就开始失控。这次我们聊的方案核心…

2026/10/15 7:33:58 阅读更多 →
WorkBuddy+WebView+Supabase:三周上线跨平台App实战与16个坑

WorkBuddy+WebView+Supabase:三周上线跨平台App实战与16个坑

1. 为什么我选择用 WorkBuddy 从零搭一个能上线的 App去年年底我接了个私活,帮一个做线下活动的小团队搭一套内部用的报名和签到工具。预算不高,工期只有三周,对方的要求却很实在:安卓和 iOS 都要能用,数据要能实时同步…

2026/10/15 7:33:58 阅读更多 →
基于Elasticsearch的高校科研系统:检索架构与实现要点

基于Elasticsearch的高校科研系统:检索架构与实现要点

1. 这个毕业设计题目好在哪:从课题价值说起每年到了毕业设计开题的时候,总有不少同学在选题上纠结很久。说实话,管理系统的题目写烂了,图书馆管理系统、学生选课系统、超市进销存系统这些基本属于“答辩老师看一眼就想翻篇”的类型…

2026/10/15 7:33:58 阅读更多 →
artcraft:本地优先的创作辅助工具,从素材管理到多形态输出

artcraft:本地优先的创作辅助工具,从素材管理到多形态输出

1. 项目缘起与整体设计思路第一次看到“artcraft”这个标题,我脑子里蹦出来的第一反应是“艺术”加“手艺”的组合词。直觉告诉我,这大概率是一个跟创意工具、手工制作、或者数字艺术创作沾边的项目。后来跟几个做独立开发的朋友聊了聊,发现大…

2026/10/15 7:33:58 阅读更多 →
Faster R-CNN PyTorch实战:从原理拆解到工业部署

Faster R-CNN PyTorch实战:从原理拆解到工业部署

1. 为什么Faster R-CNN至今仍是目标检测的“教科书级”入口你打开任何一家做视觉算法的公司招聘JD,或者翻看某高校计算机视觉课程大纲,“Faster R-CNN”这六个字母几乎从不缺席。它不像YOLO系列那样主打速度、也不像DETR那样强调架构革新,但它…

2026/10/15 7:32:58 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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/14 7:53:34 阅读更多 →