sqli-lab 38关之后:堆叠注入与ORDER BY注入实战拆解
从 Less-38 开始sqli-lab 给我的感觉一下子就变了。前 37 关基本是在一条单查询里折腾联合注入、报错、盲注、宽字节方法没变变的是过滤和闭合。到了 38 关注入点后面第一次出现了分号。这个分号让注入从“一句话里做文章”升级成了“一口气执行多条 SQL”。这篇接着记录 38 关到收尾的 Writeup重点拆解堆叠注入、ORDER BY 注入以及最后 54~65 关的挑战模式。如果你刚啃完前面 37 关准备往后刷这篇可以直接当路线图参考。1. 从38关开始第一个分水岭堆叠注入在堆什么1.1 普通注入和堆叠注入的本质区别普通注入无论怎么绕最终只能影响当前这条 SQL 语句。比如查询里写的是select * from users where id$id就算用union select把后面撑开后端执行的还是这一条 select。堆叠注入不一样它利用的是“分号结尾后还能再跟一条语句”的语义。你在参数里输入1;select sleep(3)--拼到 SQL 里就成了两条语句select * from users where id1;select sleep(3)--如果后端用的是支持多语句的接口比如 PHP 的mysqli_multi_query()MySQL 就会把分号前后的两条语句都执行掉。这跟前台看到的查询结果没关系select sleep(3)没有结果集但页面会明显卡三秒。从这一关开始题目考的不再是“怎么把查询结果拼到页面里”而是“后端到底允不允许你一次执行多件事”。1.2 为什么前面 37 关都不教堆叠并不是前面不存在这个漏洞而是 sqli-lab 的前 37 关使用的是普通查询接口源码里写的是mysqli_query()这种接口一次只能执行一条语句。你往参数里加分号MySQL 要么直接报错要么把分号后的内容忽略掉页面不会给任何反应。到了 38 关源码换成了mysqli_multi_query()教学意图很明确告诉你同一个 SQL 拼接漏洞在“单语句连接”和“多语句连接”两种模式下危害完全不同。我在实际测试里习惯把这种差距类比成电梯和楼梯普通查询像电梯你按一个楼层就只到那一层堆叠注入像是整栋楼梯你在某层打开门后还能继续往上走只要体力够走到哪一层取决于你输入多长。这个差异也是很多真实系统容易忽略的地方开发平时写 SQL 用的是普通接口上线后某个报表模块用了多语句接口一旦参数拼接点存在注入攻击面立刻变大。1.3 确认能不能堆叠只需要一个 sleep拿到一个注入点我最先做的是测闭合然后马上试;select sleep(3)。如果页面卡了 3 秒再出结果说明分号后面的第二条语句被真正执行了如果页面秒回或者直接报语法错误再考虑是不是闭合不对、是不是接口不支持多语句。这里有个容易踩的坑很多新手看到sleep()返回一个结果集会习惯性地去等页面回显那一行数字但堆叠注入的第二条查询结果一般不会显示最可靠的判断标准就是网络请求耗时。举个 Less-38 的例子。Less-38 是 GET 型单引号闭合我先请求?id1页面报错说明存在字符型注入点再请求GET /sqli-labs/Less-38/?id1;select sleep(3)-- HTTP/1.1响应时间比正常请求多了大约三秒堆叠确认。这套“测闭合 看延迟”的组合在整个 38~45 关里可以复用。2. 38~45 关堆叠注入的逐关拆解2.1 先整理一套闭合方式再打这组关卡一共有 8 关第 38~41 在 GET 参数里42~45 在 POST 参数里闭合方式基本就是前面 1~37 关重复过的那几种单引号、无引号、单引号加括号。我自己的习惯是先不急着堆叠而是用一条无害and 11和and 12确认闭合再开始堆叠。这里放一张我边刷边整理的对照表不同改版可能会有小差异但闭合和请求位置的规律是通用的关卡请求位置闭合方式探测示例Less-38GET id单引号1--Less-39GET id数字型1--Less-40GET id单引号加括号1)--Less-41GET id数字型1--Less-42POST passwd单引号1--Less-43POST passwd单引号加括号1)--Less-44POST passwd数字型1--Less-45POST passwd单引号加括号1)--如果你继续往后刷就会发现这个矩阵其实非常符合实战经验后端代码写习惯了同一个项目里很可能同时存在数字拼接和字符串拼接而且喜欢给查询条件套括号形成where (id$id)这样的结构。2.2 GET 型堆叠Less-38 到 Less-41Less-38 是整组题的样本。闭合方式确认后我在参数里拼一条 insertGET /sqli-labs/Less-38/?id1;insert into users(id,username,password) values(88,sqlinote,pass)--页面还是显示 id1 的正常数据但我用靶场自带的登录功能用sqlinote/pass登录成功说明这条 insert 真的写进了数据库。Less-39 和 Less-41 是数字型不需要单引号闭合直接从1;insert...--就能执行Less-40 是1)闭合payload 就变成1);insert...--。这三个变体值得记住的原因不是记忆 payload而是理解“怎么把 SQL 语义闭合完整”。单引号负责让你跳出字符串括号负责跳出子句后面的--负责把源码里本来会拼接的尾巴注释掉。跳得不够远后面语法就是错的注释不够干净后面内容会影响你执行的第二条语句。2.3 POST 型堆叠Less-42 到 Less-45POST 型堆叠和 GET 型在原理上完全一样区别在于参数位置从 URL 挪到了请求体而且通常出现在登录框。我习惯用抓包工具把passwd的值从1改成passwd1;insert into users(id,username,password) values(89,postnote,pass)--需要注意 POST 表单提交时的会被当作空格处理所以--到了后端其实是--正好满足 MySQL 对注释符后面必须留空的语法要求。很多新手在这里纠结“为什么写--而不是--%20”实际只要提交的是表单数据--就能正常工作如果数据是放 JSON 里传输反而优先用--或#。POST 型堆叠的应用比 GET 型更接近真实漏洞登录接口、更新资料接口、搜索框一旦使用多语句接口且参数可拼接攻击者不一定需要看到查询结果只要能执行一条 update 改掉管理员密码就足以造成危害。我在 Less-42 里就试过直接用 update 把所有用户密码改成同一个值这种“不关心回显只关心执行”的思路是堆叠注入最危险的地方。2.4 再进一步堆叠注入里的文件写入如果数据库账号有 FILE 权限并且 MySQL 的secure_file_priv允许写入指定目录堆叠注入还可以把第二条语句变成写文件操作。本地靶场里我为了验证整条链路会把 MySQL 的secure_file_priv临时设为空然后执行select ?php phpinfo();? into outfile /var/www/html/sqli-labs/shell.php写完后访问shell.php能看到 phpinfo 页面。这个例子我只建议在本地靶场里验证不要拿去测任何线上系统。它带来的思考是堆叠注入加高权限账号不只是拖库还有可能直接控制服务器这也是数据库账号权限最小化为什么那么重要。2.5 堆叠注入不能怎么玩堆叠不是万能的。第一如果后端使用 PDO 但关闭了多语句支持分号后的语句不会执行第二即使分号后的语句执行了如果第一条 select 没有结果集页面可能空白容易被误判为失败第三第二句如果是 insert 或 update对前端页面可能毫无影响需要靠其他功能验证。所以刷到后面我的验证习惯是先用 sleep 确认有没有执行再决定要不要来一条有影响的操作。3. 46~53 关ORDER BY 注入和堆叠的组合3.1 ORDER BY 为什么不能用 union从 Less-46 开始注入点变成了排序参数sortSQL 大概长这样select * from users order by $sortORDER BY 之后跟的不是查询出来的值而是一个“排序规则”所以union select那套在这里根本不成立。你可以试?sort1 union select 1,2,3但大多数情况下要么语法错误要么被当成排序列名。真正能用的只有三类报错注入、布尔盲注、堆叠注入而这三类在 46~53 关里全被过了一遍。3.2 报错注入在 ORDER BY 里怎么活数字型 ORDER BY 注入最省事的 payload 是?sort1 and extractvalue(1,concat(0x7e,database(),0x7e))因为order by后面可以跟表达式extractvalue报错内容会被页面带出来。如果排序参数有引号先闭合单引号再写and extractvalue(...)?sort1 and extractvalue(1,concat(0x7e,database(),0x7e))--我在刷 Less-47 时就是先用单引号探测出闭合再切换成报错注入。这类题的关键不是 payload 本身而是意识到“能排序的参数也是 SQL 片段的一部分”很多开发人员都默认排序参数是固定的列名结果直接拼进了查询。3.3 布尔盲注在 ORDER BY 里怎么判断如果报错内容被隐藏只能看到列表顺序那就用if表达式控制排序结果。比如判断数据库名的第一个字符是不是大于某个值?sortif(ascii(substr(database(),1,1))115,1,2)条件为真时数据按1排条件为假时按2排。通过观察第一行数据的用户名变化就能一位一位把数据库名抠出来。我习惯把这个过程和做选择题类比每条请求只回答一次“大于还是小于”然后拿二分法把答案逼近出来。Less-48、Less-49 就是这种盲注场景区别在有没有引号。3.4 在 ORDER BY 里继续堆叠Less-50 到 Less-53 又把堆叠带了回来只是在 ORDER BY 参数上。数字型可以直接?sort1;insert into users(id,username,password) values(90,orderby,pass)--有引号就先闭合?sort1;insert into users(id,username,password) values(91,orderby2,pass)--这里我体会到一件事靶场把“普通注入”和“堆叠注入”分开讲但真实系统中这两件事会叠加出现。一个排序参数如果既允许拼接又允许多语句那它就不再只是“拖库”或“写文件”而是可以直接往数据库里塞任何东西。4. 54~65 关挑战关里怎么用最少请求拿结果4.1 挑战关改动的地方到了 54 关题目会随机生成一张带 flag 的表并且每次请求次数被限制在个位数或十几位数。sqli-lab 这么设计练的是信息搜集效率你不再能靠一条条盲注慢慢猜必须在有限次数内把表名、列名、数据全拿出来。我刷挑战关之前先把前面 46~53 的报错注入方式熟悉了一遍因为报错注入可以在一次请求里带回一大段字符串比逐字符盲注效率高太多。当然挑战关也会换闭合方式所以第一步仍是拿1或1) 快速判断闭合。4.2 一次请求拿全表名以一次常见的挑战关为例闭合是单引号我直接用extractvalue报错分组函数结果?id1 and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schemadatabase()),0x7e))--页面的报错信息里会出现类似~users,emails,referers,uagents,secret_xxx~的一段内容目标表就在里面。extractvalue的报错回显通常有长度限制如果表名太多就补一个limit 0,1或者分批group_concat。4.3 再用一次请求拿列名和 flag拿到目标表名如secret_xxx后下一步用一个请求查列名?id1 and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_namesecret_xxx),0x7e))--返回内容里能看到id,something,flag之类的字段列表。接着把flag的值拿出来?id1 and extractvalue(1,concat(0x7e,(select flag from secret_xxx limit 0,1),0x7e))--一次请求一段一般三次到四次请求就能通关。如果字段值太长被截断可以用substr把值分成两段取。我遇到过有人在这里用group_concat把整张表数据都拼在报错里结果超过报错回显长度反而不如limit 0,1一条一条拿。4.4 报错被过滤就用二分法盲注如果挑战关把extractvalue、updatexml这类函数过滤了就只能回到布尔盲注。顺序仍然是先拿表名长度、再逐字符拿名字但挑战关次数有限手测几乎不可能得写脚本二分。我会在本地写个小脚本每次请求判断布尔条件用if(ascii(substr(...))x,1,0)控制排序或回显次数控制在字符集范围内的二分级别。这里最重要的不是脚本本身而是把“探测目标”拆成公式化的两步先定目标再定判断条件。4.5 挑战关对堆叠的态度挑战关一般不会要求你用堆叠因为多语句接口在真实环境里比较容易触发告警靶场更想考你单条语句内的信息提取能力。我自己刷的时候仍然会习惯性检查一次;select sleep(2)如果延迟生效说明这关也支持多语句那就可以用堆叠快速更新数据绕关如果不生效就老老实实报错或盲注。能用堆叠能节省好几条请求但不该把堆叠当默认选项。5. 常见问题与排查技巧5.1 闭合方式试探顺序我在整个 sqli-lab 后半段反复用到的试探顺序是1报错 →1报错 →1)报错 →1无报错。这个过程不是机械的而是看报错信息的样貌。如果单引号报错里带出了整段 SQL说明闭合就是单引号如果带出的是(...)说明外面套了括号如果怎么加引号都没变化大概率是数字型拼接。把这个顺序记牢后面所有关卡都能少走弯路。5.2 注释符被过滤或被解码的情况很多新手经常问“为什么--没用”。在 URL 里在某些服务端会被解析为空格所以--变成--这本来就是对的但如果 URL 经过编码层可能没转成空格MySQL 又把--后面跟上别的内容注释就不成立。更稳的做法是改用%23也就是#或者直接写--%20。POST 表单里用--比较顺因为表单解码会把加号还原为空格。注释符这块没有绝对万能答案建议在本地把三种都试一遍再记住当前环境下的偏好。5.3 堆叠注入没有延迟别急着怀疑 sleep我最初刷 38 关时遇到过sleep(3)不生效的情况后来发现是闭合没对语句语法本来就是错的自然谈不上延迟。如果闭合对了但还不生效再看接口是不是多语句。sqli-lab 在 38~53 的源码固定用多语句接口但如果你把同样的 payload 放到前面 1~37 关就不会延迟。只需要分清“语法错误”“不支持多语句”“语句执行但无回显”这三种状态排查会快很多。5.4 group_concat 和报错函数的长截断extractvalue和updatexml报错输出的长度有限大概只有几十个字符group_concat默认长度也可能只有 1024。数据一长就会被截断页面看到的一堆查询结果里只有一半。解决方法是给目标数据分段select substr(group_concat(...),1,64)、select substr(group_concat(...),65,64)。另一个经验是报错注入拿数据时尽量让字符串短一点用concat_ws控制分隔符而不是把所有字段都堆在一起。5.5 堆叠更新完数据页面没变化用 insert 或 update 验证堆叠时很多新手会说“明明执行了但页面没有任何变化”。这是因为第二条语句操作的是数据库不是当前查询结果。你可以去调用一个读取该数据的功能或者再看一次数据库表别指望当前页面会帮你打印提示。如果在靶场里只是验证最直观的还是;select sleep(3)延迟本身就是证据。6. 把后半段这些套路翻译成防御手段6.1 多语句接口是堆叠注入的土壤sqli-lab 从 38 关开始能堆叠不是因为 MySQL 本身有问题而是 PHP 选择了mysqli_multi_query()。真实项目里如果不需要一次执行多条语句就不应该开这个口子。开发用 PDO 的时候以为自带防护其实某些 PDO 配置也会允许多语句稍不留神就会踩坑。防御上最直接的写法是查询一律用预处理语句连接配置里显式关闭多语句排序参数走白名单不接受用户自由拼接。6.2 账号最小权限能挡掉一半写操作堆叠注入最疼的不是多查一条数据而是多执行一条 update 或者写一个文件。数据库账号只要不持有 FILE 权限into outfile这招基本失效只要没有 update 和 insert 权限堆叠造成的破坏面就小很多。靶场里你可能会觉得堆叠很好用但真实数据库一旦让业务账号拥有所有权限一次注入连备份都可能被删掉。最小权限原则不是上线规范里的一句口号而是渗透测试里最实在的止损手段。6.3 监控和告警比 WAF 更靠谱挑战关里的报错注入和布尔盲注特征其实很明显大量请求里反复出现information_schema、extractvalue、updatexml、ascii(substr(而且同一参数的值逐字符变化。与其依赖 WAF 的规则库不如在数据库审计日志里对这类关键词做统计请求频率异常时直接告警。我自己刷完这二十几关最大的感受是漏洞利用的变形能力很强但“在数据库层面对异常 SQL 进行审计”这条基线从来没变过。这套后半段刷下来的经验我自己最想留下的不是某一条 payload而是那个排查顺序先判断注入点是什么拼接形态再判断能不能堆叠最后才决定用报错还是盲注。sqli-lab 把同一个知识点翻来覆去换了二十遍包装就是在训练这种肌肉记忆。如果你也在刷某一个系列别急着背答案建议每关在笔记里画一张参数、闭合、接口模式的表格刷到后面会发现很多东西都是排列组合。

相关新闻

企业互访的商务逻辑:从信号识别到试点落地

企业互访的商务逻辑:从信号识别到试点落地

企业互访这件事,表面上看就是双方高管握手拍照发新闻稿,但实际上每一次有分量的到访,背后都有一套清晰的商务逻辑在推动。我复盘过不少典型的互访案例,其中“某区域供应链综合服务企业高管团队到访某产业互联网平台公司”这类场景…

2026/10/17 17:15:19 阅读更多 →
基于Apache Doris的数据中台建设:从选型到落地的完整规划

基于Apache Doris的数据中台建设:从选型到落地的完整规划

简介:基于Apache Doris的数据中台建设思路规划,是一份面向大数据从业者与企业管理者的完整参考文档,系统解决了从数据中台概念理解到技术落地过程中的关键问题。内容首先厘清数据中台与数据仓库、数据平台的区别,并详细解释主题域…

2026/10/17 17:14:18 阅读更多 →
Bootstrap+Vue实现在线考试系统:答题、倒计时、判分与状态恢复

Bootstrap+Vue实现在线考试系统:答题、倒计时、判分与状态恢复

这段时间帮人做了一套带课程测验性质的小系统,需求不算复杂:题目展示、选项作答、倒计时交卷、分数统计,再把错题整理出来。技术选型上没犹豫,Bootstrap 加 Vue 这个组合足够稳妥。Bootstrap 负责栅格布局、卡片、进度条和各类表单…

2026/10/18 20:03:15 阅读更多 →

最新新闻

AI绘画入门指南:从部署到提示词调优的完整实战

AI绘画入门指南:从部署到提示词调优的完整实战

1. 先搞清楚这玩意儿到底能干嘛1.1 从“输入一句话,蹦出一张图”说起第一次接触这个工具的人,脑子里冒出来的画面多半是:我敲几个字,它给我画一张画。没错,核心逻辑就这么简单。但真正上手之后你会发现,事情…

2026/10/18 20:07:42 阅读更多 →
终端保密检查工具实战指南:配置、避坑与常态化检查

终端保密检查工具实战指南:配置、避坑与常态化检查

简介:面向Windows终端安全保密管理场景的检查工具,适合机关单位、企业保密办及IT运维人员开展敏感信息自查。压缩包内共9个文件,主体为可执行主程序与配套动态链接库,同时包含关键词扫描规则、补丁数据及设备标识等配置&#xff0…

2026/10/18 20:07:42 阅读更多 →
OpenAI开放GPT-6智能界面:从对话到多模态协作的范式升级

OpenAI开放GPT-6智能界面:从对话到多模态协作的范式升级

1. 本期头条:OpenAI开放GPT-6智能界面,信息密度比想象中高假期回来盯着后台,看到群里已经炸了一圈:OpenAI正式开放了GPT-6智能界面。也就是说,这版被外界反复猜测了大半年的交互形态,终于从实验室和灰度测试…

2026/10/18 20:07:42 阅读更多 →
大文件上传方案:分块上传与断点续传完整实战解析

大文件上传方案:分块上传与断点续传完整实战解析

做了几年Java Web开发,最怕接到的需求之一就是"上传一个几百MB甚至几个GB的文件"。最开始都是直接丢给后端一个POST接口,一分钟的文件传二十分钟还中途断掉,用户直接开骂。后来把所有文件上传需求都统一改成了"分块上传断点续…

2026/10/18 20:07:42 阅读更多 →
腾讯云语音识别+Python:打造自动会议纪要工具全指南

腾讯云语音识别+Python:打造自动会议纪要工具全指南

开会两小时,整理纪要一下午,这句话在职场上几乎没人能笑着读完。最近我给自己所在的团队做了一套内部工具,核心就是用腾讯云AI语音识别把会议录音自动转成文字稿,再按说话人拆好段落、清理语气词、生成标准的会议纪要。折腾下来大…

2026/10/18 20:07:42 阅读更多 →
改进粒子群优化LSSVM的短期电力负荷预测及GUI设计

改进粒子群优化LSSVM的短期电力负荷预测及GUI设计

简介:基于改进粒子群优化最小二乘支持向量机(LSSVM)的短期电力负荷预测Matlab实现,面向电子信息、计算机、数学等专业学生,可用于课程设计、期末大作业及毕业设计。压缩包内共38个文件,涵盖19个m源码文件、…

2026/10/18 20:06:42 阅读更多 →

日新闻

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

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

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

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

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

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

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

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

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

2026/10/18 0:00:55 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 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/18 0:55:02 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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 阅读更多 →