下载能接着上次继续:Range 与 206 怎么配合
授权与合规声明本文全部操作对象均为自建隔离靶场本机容器或隔离虚拟机涉及安全测试的环节必须以取得合法授权为前提。未经授权的渗透测试违反《中华人民共和国网络安全法》与《刑法》相关条款须承担相应法律责任。本文只讲环境配置、版本对照与靶场隔离不含任何攻击步骤、利用载荷与绕过手法请勿将文中环境指向任何非自有系统。一、断线之后重头再来浪费的是什么1.1 那个继续下载的按钮一个大文件下到中途网断了或者你自己按了取消进度条卡在半路。下载器上常有一个继续入口点下去它从刚才停下的地方接着走而不是从 0 重来。这套机制为什么存在规范在开头就交代了RG01原文说客户端经常因为请求被取消或连接掉线而遇到被中断的数据传输当客户端已经存下了一份表示的部分内容时它更希望在后一个请求里只取回那份表示的剩余部分而不是把整份重新传一遍。把两个词拎出来地基就出来了一个是客户端已经存下了一部分另一个是只要剩下的那截。1.2 它解决的不是重试而是接着传这里最易混的是两个动作重试把同一个请求原样再发一遍从头来过前面的努力等于白费续传手里已经有一段新请求表达的是我要的是剩余的那部分目标不是同一段数据。规范那句request the remainder of that representationRG01说的正是第二种。有了它断线的代价从整份重来降到补上缺的那截。⚠️代码待验证# 两个容易混的动作本文未在本机运行# 重试 retry : 原样再发同一个请求 - 重新取整份# 续传 resume : 新请求只取「剩余部分 remainder」- 接着传1.3 前提是一个存下来的动作已经存下这个前提值得单说先有客户端侧留下的一段内容才谈得上请求剩余部分RG01。所以下载器里保留临时文件不是可有可无的细节——临时文件没了接着传就失去了依据。清理下载目录时删掉没下完的那截往往意味着这个任务只能重来。本章可以带走的一句断点续传的官方理由写在规范开头——客户端已经存下一部分表示时希望后一个请求只取回剩余的那截它省下的正是整份重传的那一遍。二、它是可选特性所以要先把话说清楚2.1 规范一上来就给定性OPTIONAL规范先交代这件机制在 HTTP 里的地位RG02范围请求是 HTTP 的一个可选OPTIONAL特性它被这样设计——没有实现这个特性的接收方或者对目标资源不支持它的接收方可以像处理普通 GET 请求那样作答而不会影响互操作。“可选落到两个地方没实现这个特性服务器压根不做范围处理对目标资源不支持服务器整体支持但对这一个资源不给分段。无论哪种接收方都应能把带范围的请求当成普通请求对待两边照样能通——这就是不影响互操作”。2.2 为什么非要单开一个状态码第二个要点规范说部分响应要用一个独立的状态码表示这样那些可能没实现该特性的缓存就不会把部分响应误当成完整响应RG02。这里只取一层结论若部分响应与完整响应共用一个码读的人以及中间的缓存就没法从状态码本身区分这是一整份还是这是其中一截。独立的码把这是部分顶到了最前面。至于缓存怎么据此判定是另一条轴本文不展开。2.3 可选落到读者身上是三条心态对客户端不能假定服务器一定支持也不能假定支持对每个资源都成立对服务端可以完全不理会范围请求下一章会看到规范用的原词是 MAY对读抓包的人先看状态码判断是部分还是完整而不是先看内容长度。本章可以带走的一句范围请求在 HTTP 里被明确定义为可选特性——没实现的接收方可以当普通 GET 处理部分响应用一个独立状态码标出来为的是不被没有实现该特性的缓存误当成整份。三、一个概念贯三个字段3.1 range unit一把量长度的尺子范围这套东西翻来覆去就三个字段但背后是同一个概念。规范把它叫range unit范围单位并一次讲清它用在哪三处RG03这个范围单位的通用概念被用在Accept-Ranges§14.3这个响应头里宣传支持范围请求被用在Range§14.2这个请求头里划出想要表示的哪些部分还被用在Content-Range§14.4头里描述正在传的是哪一部分。三个字段说的是同一件事的三个侧面——用哪把尺子、要尺子上的哪一段、正在给的是哪一段。规范还给了两点形式约定range-unit token且所有范围单位的名字都大小写不敏感RG03。字段写在哪条消息里它的作用共同的底子Accept-Ranges响应宣传支持范围请求同一个 range unitRange请求划出想要表示的哪些部分同一个 range unitContent-Range响应描述正在传的是哪一部分同一个 range unit3.2 这把尺子上能写哪几种范围规范在下一小节给出三种形态RG04int-range写作first-pos - [ last-pos ]给一个起点、终点可选suffix-range写作- suffix-length只给一个从末尾倒数的长度other-range是留给扩展的位置。什么叫无效的判据很好记当 int-range 的last-pos存在、且小于first-pos时这个 int-range 无效RG04——起点跑到终点后面去了范围自然立不住。是否能满足也有判据一个有效的 ranges-specifier只要含有至少一个可满足的 range-spec就是satisfiable否则是unsatisfiableRG04。记法就是看有没有至少一个能满足的段。⚠️代码待验证# 三类 range-spec 的写法照 RFC 9110 §14.1.1 记本文未在本机运行# int-range first-pos - [ last-pos ] # 给起点终点可选# suffix-range - suffix-length # 只给从末尾倒数的长度# other-range # 扩展位留给其它范围单位# 无效int-range 的 last-pos 存在且小于 first-pos# 可满足范围内含有「至少一个」可满足的 range-spec3.3 bytes 这把尺子的两个约定最常用的尺子叫bytes。规范给它定了两条约定RG05第一last-pos给的是范围内最后一个字节的偏移——位置是闭区间字节偏移从 0 开始闭区间意味着两个端点都算在内这是看数字时评错一格的根源。第二如果表示数据带上了内容编码每个字节范围都是相对编码后的字节序列来算的而不是解码之后才得到的那串字节RG05。第二条只取按编码后的字节算这一句内容编码本身怎么协商、怎么解是另一条轴本文不展开。你只要记住范围数字落在传输时那串字节上。本章可以带走的一句Accept-Ranges、Range、Content-Range三个字段共用同一个 range unit这类尺子的位置是闭区间、偏移从 0 起算而且按编码后的字节计数。四、请求侧Range 和它的两个前置4.1 Range 只对 GET 定义规范对Range头的定性是放在一个 GET 请求上的Range会改变方法语义让它请求传输所选表示数据的一段或多段子范围而不是整份表示RG06。接着是两档完全不同的忽略可以忽略MAY——服务端可以忽略Range头字段RG06支持不支持最终由它定必须忽略MUST——对方法不被识别、或者没有定义范围处理的方法服务端必须忽略收到的Range而且在本规范里GET 是唯一定义了范围处理的方法RG06。情形规范用词结果支持范围处理的服务端MAY可以忽略可以不理会方法不被识别MUST必须忽略方法没定义范围处理MUST必须忽略本规范范围内——只有 GET 定义了范围处理⚠️代码待验证# Range 头被处理的两档照 RFC 9110 §14.2 记本文未在本机运行# MAY 忽略 : 服务端可以不理会 Range# MUST 忽略 : 方法不被识别 / 未定义范围处理时# 范围处理 : 本规范里只有 GET 被定义4.2 两个前置先算前置条件再轮到 RangeRange不是无条件生效的。规范给它排了先后RG07Range是在评估完 §13.1 定义的前置条件头字段之后才被评估的并且只有在没有Range头时结果本来会是一个 200 (OK)的情况下才轮到它换句话说当一次条件式 GET 会得到 304 (Not Modified) 响应时Range被忽略。这一条本文只取先后顺序前置条件先算只有本来会是 200才轮到Range生效。至于条件式请求为什么可能得到 304、ETag与If-None-Match如何判定属于另一条轴本文一个字都不展开。第二个前置是If-Range。规范的另一句是If-Range头字段§13.1.5可以被用作施加Range头字段的前置条件RG07。要老实交代口径规范另有前置条件专节If-Range可以充当它的前置条件而这一节的本体本次未核NB7。所以本文到此为止只写它能当Range的前置条件这个存在关系。4.3 服务端怎么答两个 SHOULD外加不保证给全轮到服务端作答规范给的是两个应当SHOULDRG08对请求里那些可满足的 range-spec服务端应当回一个 206 (Partial Content) 响应内容含对应的一段或多段部分表示否则应当回一个 416 (Range Not Satisfiable) 响应。但规范紧接着泼了盆冷水RG09上面这些并不代表服务端会把请求的所有范围都发出来。有些情况下先只发一部分可能才是可行或高效的规范期望客户端在仍然需要剩余部分时之后再发起请求。所以一次请求、一次给全从来不是这套机制承诺的东西。本章可以带走的一句Range只对 GET 定义服务端对它是可以忽略方法不被识别或没定义时是必须忽略它排在其它前置条件之后评估只有本来会是 200才生效而且服务端不保证一次把所有范围都给全。五、响应侧三个字段要一起读5.1Accept-Ranges只是建议不是承诺先看最常被误读的字段。规范对Accept-Ranges的定性是响应里的这个字段表示上游服务端是否支持对目标资源的范围请求RG10——注意它说的是上游服务端是否支持是一句告知。规范划了两条边界RG10客户端可以不管有没有收到Accept-Ranges照样发范围请求这个字段的信息只是建议为的是改善性能、减少不必要的网络传输反过来客户端也不能假定收到了Accept-Ranges就意味着将来的范围请求一定会返回部分响应因为内容可能变、服务端可能只在某些时候或条件下才支持、也可能是另一个中间件在处理下一个请求。还有一句对目标资源完全不支持任何范围请求的服务端可以发Accept-Ranges: none而none这个范围单位名就是为这个用途保留的RG10。5.2Content-Range把这一段是哪一段写清楚第二个响应字段是Content-Range。规范给的语法是RG11Content-Range range-unit SP ( range-resp / unsatisfied-range )其中range-resp incl-range / ( complete-length / * )unsatisfied-range */ complete-length。翻译一下它开头先写用的是哪把尺子range unit然后要么给一段包含范围 / 完整长度要么给一个*/完整长度的不满足形态。对字节范围规范还有两句要点RG11发送方应当给出范围被提取出来的那份表示的完整长度除非完整长度未知或难以确定用一个星号*代替 complete-length就表示完整长度未知。这里还藏着一条硬边界如果一个 206 (Partial Content) 响应带着一个接收方不认识的 range unit的Content-Range接收方不得尝试把它与已存下的表示重新组合RG11。尺子不认识就别硬拼——拼出来的东西没有任何保证。5.3 206 里的Content-Length不等于完整长度最要紧的一条来自 206 的定义本身。规范说RG12206 (Partial Content) 状态码表示服务端正在成功满足一个针对目标资源的范围请求方式是把所选表示的一部分或多部分传过来。接着是必须划出来的一句客户端必须检查 206 响应里的Content-Type和Content-Range字段以确定这一条消息里包了哪些部分以及是否还需要再发请求RG12。然后是题眼级别的一句206 响应里出现的Content-Length表示的是这条消息内容里的八位组数而这个数通常并不是所选表示的完整长度完整长度信息在每个Content-Range头字段里RG12。所以读一条 206别拿Content-Length当整份文件的大小——它只说明这一条消息里带了多少。字段它回答的问题别拿它当什么Accept-Ranges上游支持吗建议性不能当将来一定给部分响应的承诺Content-Range这一段是哪一段、整份多长——完整长度信息在这里206 里的Content-Length这一条消息里带了多少不是整份表示的完整长度完整版协议对照表这一章的Accept-Ranges只是建议、Content-Range语法与完整长度、206 里Content-Length的读法连同第三章三类 range-spec 的写法一起收进资料包扫码即可获取本章可以带走的一句Accept-Ranges只是建议而非承诺读一条 206先看Content-Range拿这一段是哪一段、整份多长别把这条响应里的Content-Length当成整份文件的大小。六、什么时候不该给416 与拒绝病态请求6.1 416 的两种触发服务端不给部分响应时会用到 416。规范对它的定义是RG13416 (Range Not Satisfiable) 表示请求Range头字段里那组范围被拒绝了原因要么是所请求的范围里没有一个可满足要么是客户端请求了数量过多的小范围或相互重叠的范围这是一种潜在的拒绝服务攻击。请注意它列的是两个并列的原因一个偏你要的根本没有一个偏你问的方式本身有问题。规范还补了一条应当做的动作RG13对字节范围请求回 416 的服务端应当生成一个Content-Range头字段用来说明所选表示此刻的长度——即便这次没给成也把我这儿此刻多长交代清楚。6.2 服务端为什么得有权拒绝病态请求范围请求有一个绕不开的风险请求本身可以被拿来消耗资源。规范因此给了一张可拒绝清单RG16支持范围请求的服务端可以忽略或拒绝一个Range头字段如果它含有一个无效的 ranges-specifier、两个以上相互重叠的范围、或者一组数量多、又没有按升序排列的小范围。理由规范也写了这些要么是客户端坏了的迹象要么是一次蓄意的拒绝服务攻击RG16。同时规范给客户端提了一句应当RG16客户端不应当请求多个范围如果这些范围处理与传输起来本来就比用一个范围把它们全包住更低效——也就是说多段请求应当尽量升序别故意拼得又碎又乱。本文只取这两点不给任何一个范围请求的构造样例。被拒的情形规范怎么看待它无效的 ranges-specifier客户端坏了或蓄意两个以上相互重叠的范围潜在拒绝服务多、且未升序的小范围低效或蓄意构造6.3 上传不能续和 curl-C的官方语义范围这套东西主要服务于取。规范另有一处存在性说明Content-Range被当作请求修饰符用来做 partial PUT 时它是基于客户端与源服务器之间的私有约定而且对没有定义 Content-Range 支持的方法服务端必须忽略请求里收到的Content-RangeRG14。这一句点到为止本文不给任何用法。落到命令行最常见的一个开关是 curl 的-C。官方 man page 写得相当明确RG15续传的意思是从给定的字节偏移接着传而这个偏移是源文件开头起算、被跳过的字节数用-C -是让 curl 自己根据输入 / 输出文件去判断从哪里接着传HTTP 上用 POST 或 PUT 的上传不能续传——把选项和这类上传合在一起时curl 会直接报错它与--range互斥一次传输里只能二选一另外--no-clobber与--remove-on-error不能与-C一起用。本文引用的 curl 官方 man page页面自述的版本号为 8.23.0截至 2026-10-09。⚠️代码待验证# curl 续传选项的语义照官方 man page 记本文未在本机运行# -C offset 从给定字节偏移继续# 该偏移是「源文件开头起算、被跳过的字节数」# -C - 让 curl 自己从输入/输出文件推断从哪里接着传# HTTP 上传限制POST / PUT 的上传不能续传合并时会直接报错# 互斥与 --range 只能二选一一次传输# 其他--no-clobber 与 --remove-on-error 不能与 -C 同用本章可以带走的一句416 有两种触发——要么没有一段可满足要么小范围太多或互相重叠服务端据此有权拒绝无效与病态的 range 集多段请求应升序而上传走 POST / PUT 时不能续传curl 的-C与--range只能二选一。七、从抓包到结论一张读表法7.1 先按响应码分三档一条和下载有关的响应落到眼前先别急着看数字先看状态码——这套机制把整份还是部分放在了码上RG02、RG12、RG13看到200是一条完整响应也可能是一条把范围请求当普通 GET 处理的响应看到206服务器正在成功地满足一个范围请求传的是一部分看到416那组范围被拒绝了要么无可满足要么被判定为病态。7.2 把这张表对着抓包读先看到再确认什么得出的结论Accept-Ranges出现 /none这只是建议RG10有不能推出一定给部分none是明确不支持一条 206看Content-Range的 range unit 与完整长度RG11、RG12这一段是哪一段、整份多大206 里的Content-Length想起来它只是这条消息的长度RG12别当成整份文件大小Range与条件请求同来Range排在其它前置条件之后RG07只有本来会是 200才轮到它一组重叠 / 未升序的小范围服务端可忽略或拒绝RG16可能直接回到 416真要按顺序走就四步先认码200 / 206 / 416→ 再看Content-Range拿哪一段、多长→ 把Content-Length与完整长度分开看 → 最后才回头看请求侧那两个前置条件。⚠️代码待验证# 读一条续传响应的四步本文未在本机运行# 1) 认码 : 200 / 206 / 416# 2) Content-Range : 哪一段、整份多长# 3) Content-Length: 只算「本条消息」的长度# 4) 回头再看请求侧那两个前置条件7.3 两条要老实交代的边界本文只用到已核到的一手材料有两处必须标清楚条件请求的前置条件本体§13.1 / §13.1.5 里If-Range的完整判定本文未核NB7——本文只取了Range排在前置条件之后、If-Range可作其前置条件这一先后与存在关系multipart 206§14.6里的逐段语义本文未核NB8——一条 206 里可能承载多个部分其逐段如何组织本次没有核到本体因此本文不写它的细节。把这两条摆在明处是为了让这张读表法停在它该停的地方已核的照写未核的标明不靠记忆补。完整版协议对照表这一章的从抓包到结论读表法、四步顺序、两条未核边界加上前面各章的字段口径一并收进资料包扫码即可获取本章可以带走的一句读一条续传相关的响应按先认码200 / 206 / 416→ 再看Content-Range拿哪一段多长 →Content-Length另算 → 最后才看请求侧前置条件四步走If-Range判定与 multipart 逐段语义本文未核。附表 A本文引用事实与官方出处对照表RG 编号事实陈述一手出处小节本文位置RG01客户端常因请求取消或连接掉线遇到被中断的传输已存下一份表示的部分内容时宜在后一个请求里只取回剩余部分而非整份重传RFC 9110 §14序言第 1 章RG02范围请求是 HTTP 的可选特性未实现该特性或对目标资源不支持者可当普通 GET 处理而不影响互操作部分响应用独立状态码表示避免被未实现该特性的缓存误当作整份RFC 9110 §14第 2、7 章RG03range unit概念被用于Accept-Ranges宣传支持、Range划出想要的部分、Content-Range描述正在传的部分range-unit token名字大小写不敏感RFC 9110 §14.1第 3 章RG04int-range first-pos - [ last-pos ]、suffix-range - suffix-length、other-range为扩展位last-pos存在且小于first-pos时该 int-range 无效含至少一个可满足 range-spec 即可满足否则不可满足RFC 9110 §14.1.1第 3 章RG05last-pos给出范围内最后一个字节的偏移位置是闭区间字节偏移从 0 开始表示数据带内容编码时每个字节范围按编码后的字节序列计算RFC 9110 §14.1.2第 3 章RG06Range放在 GET 上会改变方法语义请求一段或多段子范围而非整份服务端 MAY 忽略对方法不被识别或未定义范围处理者 MUST 忽略本规范里 GET 是唯一定义范围处理的方法RFC 9110 §14.2第 4 章RG07Range在 §13.1 前置条件头字段评估之后才评估且仅当没有Range时本来会是 200才生效即条件式 GET 会得到 304 时Range被忽略If-Range§13.1.5可作施加Range的前置条件RFC 9110 §14.2第 4、7 章RG08对可满足的 range-spec服务端 SHOULD 回 206 并含对应的一段或多段部分表示否则 SHOULD 回 416RFC 9110 §14.2第 4 章RG09上述不意味着服务端会发送全部请求范围可能只先可行/高效地发一部分期望客户端之后按需再请求剩余部分RFC 9110 §14.2第 4 章RG10Accept-Ranges表示上游服务端是否支持对目标资源的范围请求客户端 MAY 不管有无该字段而发范围请求该信息只作建议客户端 MUST NOT 假定收到它就意味着将来一定返回部分响应不支持者可发Accept-Ranges: nonenone为保留取值RFC 9110 §14.3第 5、7 章RG11Content-Range range-unit SP ( range-resp / unsatisfied-range )、range-resp incl-range / ( complete-length / * )、unsatisfied-range */ complete-length对字节范围发送方 SHOULD 给出完整长度星号表示完整长度未知Content-Range的 range unit 接收方不认识时 MUST NOT 与已存表示重组RFC 9110 §14.4第 5、7 章RG12206 表示服务端正成功满足一个范围请求传一部分或多部分客户端 MUST 检查 206 的Content-Type与Content-Range以确定包了哪些部分、是否还需再请求206 里的Content-Length是本条消息内容的八位组数通常不是所选表示的完整长度完整长度在Content-Range里RFC 9110 §15.3.7第 5、7 章RG13416 表示请求Range里的范围集被拒或因无可满足范围或因客户端请求了过多小范围或重叠范围潜在拒绝服务攻击对字节范围请求回 416 的服务端 SHOULD 生成Content-Range说明该表示此刻的长度RFC 9110 §15.5.17第 6、7 章RG14Content-Range作请求修饰符做 partial PUT 时基于客户端与源服务器的私有约定对未定义 Content-Range 支持的方法服务端 MUST 忽略请求里的Content-RangeRFC 9110 §14.5第 6 章RG15curl-C从给定字节偏移续传该偏移是源文件开头起算、被跳过的字节数-C -由 curl 自行判断从哪里接着传HTTP 的 POST / PUT 上传不能续传合并时会报错与--range互斥--no-clobber与--remove-on-error不能与-C同用curl 官方 man page自述版本 8.23.0第 6 章RG16服务端 MAY 忽略或拒绝含无效 ranges-specifier、两个以上重叠范围、或大量未升序小范围的Range因其为客户端损坏或蓄意拒绝服务的迹象客户端 SHOULD NOT 请求处理与传输比单个范围更低效的多个范围RFC 9110 §14.2第 6、7 章附表 B术语速查表术语一句话解释range unit范围单位量范围的尺子贯Accept-Ranges/Range/Content-Range三个字段名字大小写不敏感Accept-Ranges响应头告知上游是否支持范围请求只是建议none是明确不支持的保留取值Range请求头写在 GET 上划出想要表示的哪些部分服务端可忽略方法不对时必须忽略Content-Range响应头描述正在传的是哪一部分、整份多长不认识的 range unit 不得重组int-range/suffix-range前者first-pos - [ last-pos ]后者- suffix-length可满足 / 不可满足ranges-specifier 含至少一个可满足 range-spec 即可满足否则不可满足闭区间last-pos是范围内最后一字节的偏移两个端点都算在内偏移从 0 起206Partial Content服务端成功满足范围请求传的是一部分Content-Length是这条消息的长度416Range Not Satisfiable范围集被拒无可满足范围或请求了过多/重叠的小范围潜在拒绝服务前置条件先于Range评估的头字段只有本来会是 200才轮到Range生效If-Range可作施加Range的前置条件其判定本体本文未核curl-C从给定字节偏移续传-C -自行判断与--range互斥上传不能续传代码待验证本文标记该命令未在本机实际运行过写在最后这篇用到的资料写这篇文章时我一直在琢磨那个不起眼的小动作——下载器上那个继续键按下去背后其实是一整套协议在支撑客户端先存下一截再用一个只取剩余部分的请求把它补完。把Range、206、Content-Range放到一起读才发现它们共用的是同一把尺子。顺手也整理了几份配套的东西靶场环境对照表DVWA、upload-labs 在 Windows / macOS / Linux 三平台的可行性与推荐路径Web 安全学习路线图从基础打牢到安全管理四个阶段各学什么常用靶场清单每个靶场练什么、适合哪个阶段资料是我自己整理的放在下面这个码上扫码即可获取添加时备注「靶场」优先通过。拿到之后建议先看环境对照表那一份把本文讲的三个字段、206 的读法、curl 续传的边界对照着过一遍下次再看到抓包里那条 206就知道该从哪里读到我在收的是哪一截。