curl--proto选项全解:精确限制传输协议、防御重定向攻击的实战指南
【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl
--proto是 curl 命令行工具中用于限定传输协议白名单/黑名单的核心安全选项,它允许你在发起请求前显式声明"本次传输允许使用哪些协议、禁止使用哪些协议"。本指南以 curl 仓库中 tests/data/data1705-3.md 的官方文档为主线,结合 src/tool_getparam.c、src/tool_paramhlp.c 等源码实现与 tests/data/test1474、tests/data/test322 等测试用例,系统讲解--proto的语法、三种修饰符语义、与--proto-redir/--proto-default的配合方式,以及用-all一键禁用的安全实战方案。
一、--proto是什么:一次传输的协议准入清单
--proto自 curl 7.20.2 起提供,属于 "connection curl" 类别,其作用是限制一次传输中允许使用的协议集合。它的核心语义可以概括为:
- 协议列表从左到右依次求值,逗号分隔;
- 每一项是一个协议名或关键字
all; - 每一项可以带零个或多个修饰符前缀(
+、-、=),不带修饰符时等价于+(默认追加许可)。
官方文档给出的定义是 "Limit what protocols to allow for transfers",即它控制的是"这次 curl 调用允许碰哪些协议"。它不会改变 curl 对 URL 中 scheme 的解析结果,而是决定解析出来的协议是否被放行:被禁止的协议即使写在 URL 里也会被拒绝。
二、三种修饰符:+、-、=
修饰符是--proto的语法核心,必须准确理解三者差异:
| 修饰符 | 语义 | 对既有许可集合的影响 |
|---|---|---|
+(或省略) | 许可该协议 | 在已许可的集合上追加 |
- | 拒绝该协议 | 从已许可的集合中移除 |
= | 只许可该协议 | 清空已有集合,仅保留本次指定的(可被后续项再修改) |
官方文档中的示例:
--proto -ftps 使用默认协议集合,但禁用 ftps --proto -all,https,+http 只启用 http 和 https --proto =http,https 也只启用 http 和 https第一条示例中,-ftps表示"沿用默认许可集合,但把 ftps 剔除"。默认许可集合由 curl 构建时启用的全部协议构成(详见下文源码分析)。第二、三条示例虽然写法不同(一个用-all清空再添加,一个用=清空再指定),但最终效果一致:都只放行 http 和 https。
三、all关键字与"未知协议"的宽容策略
3.1all的含义
all不是某个具体协议名,而是"当前 curl 二进制中构建进的所有协议"的集合别名。它同样可以搭配修饰符使用:
-all:清空许可集合(即"什么都不允许");+all/=all:重新填满全部协议。
3.2 对未知/禁用协议的处理:警告而非报错
官方文档明确指出:
Unknown and disabled protocols produce a warning.
即:当你用--proto -xxx禁用一个当前 curl 并未构建的协议(例如一个没有编译 FTP 支持的 curl 里写--proto -ftp)时,curl 不会报错退出,而是给出警告并继续执行。这一设计非常关键——它让脚本可以安全地"一刀切"禁用潜在危险协议,而不必关心当前二进制到底编译了哪些协议。如果 curl 对不存在的协议直接报错,那么同一套禁用脚本在不同构建环境的 curl 上行为就会不一致,甚至无法运行。
3.3 与"未知协议"报错的边界
需要区分:--proto列表里写了一个不存在的协议名(例如--proto +-http这种修饰符误用,或写了--proto httpss),此时proto2num()会输出unrecognized protocol错误并返回PARAM_BAD_USE,curl 以退出码 2 失败。这一点被 tests/data/test322 精确锁定:
curl: unrecognized protocol '-http' curl: option --proto: is badly used here也就是说,"禁用未构建协议"走警告路径,而"写了一个根本不存在的协议名"走硬错误路径——两者不能混为一谈。
四、重复使用与等价合并
官方文档说明--proto可以多次出现,多次使用的效果等价于把所有协议项按顺序拼接进同一次选项:
curl --proto -ftp --proto -smb http://example.com # 等价于 curl --proto -ftp,-smb http://example.com这在脚本生成命令时很实用:可以按模块逐步累积协议策略,而不必在单个参数里手工维护一长串列表。
五、源码级实现:--proto是如何被解析的
5.1 参数入口
在 src/tool_getparam.c 中,--proto的解析入口如下:
case C_PROTO: /* --proto */ config->proto_present = TRUE; err = proto2num(built_in_protos, &config->proto_str, nextarg);它调用 src/tool_paramhlp.c 中的proto2num(),将用户的协议表达式转换成最终的协议字符串,并存入config->proto_str,随后由--libcurl生成逻辑映射为 libcurl 的CURLOPT_PROTOCOLS_STR(见 src/config2setopts.c)。
5.2 默认协议集合来自运行时信息
built_in_protos并非硬编码常量,而是 curl 启动时通过curl_version_info()获取的运行时协议列表。在 src/tool_libinfo.c 的get_libcurl_info()中:
curlinfo = curl_version_info(CURLVERSION_NOW); ... built_in_protos = curlinfo->protocols; ... proto_count = builtin - built_in_protos;也就是说,all到底包含哪些协议,取决于当前 curl 二进制实际编译进了哪些协议支持——这正是上文"禁用未构建协议只给警告"这一宽容策略能成立的前提。
5.3 从左到右求值的具体实现
proto2num()维护一个临时的protoset协议集合数组,逐项扫描逗号分隔的输入(src/tool_paramhlp.c):
switch(*str++) { case '=': action = set; /* 清空后仅保留 */ break; case '-': action = deny; /* 从集合移除 */ break; case '+': action = allow; /* 追加许可 */ break; default: /* 无修饰符,等价于 allow */ break; }对应all的处理(src/tool_paramhlp.c):deny时清空protoset[0] = NULL;allow/set时把全部内置协议复制进集合。单个协议名则经proto_token()校验后执行对应动作,最后结果按字母序排序并拼接成最终字符串(src/tool_paramhlp.c)。
5.4 测试用例佐证
- tests/data/test1474 验证
--proto -all会禁用全部协议:命令--proto -all http://...直接以退出码 2(PARAM_BAD_USE)失败——因为许可集合为空,任何请求都无法发起; - tests/data/test322 验证修饰符与协议名之间不能有歧义:
--proto +-http被解析为未知协议-http并报错退出; - tests/data/test1245 验证
--proto的拒绝优先级高于--proto-redir的许可:--proto +all,-ftp --proto-redir -all,+ftp组合下,--proto中已禁用的 ftp 不会被--proto-redir重新放行; - tests/data/test1401 展示了
--proto "=http,ftp,file"在--libcurl代码生成场景下的合法写法。
六、安全实战:重定向场景下的协议白名单
6.1 问题的由来
--location(-L)会让 curl 跟随服务器返回的 3xx 重定向(详见 docs/cmdline-opts/location.md)。攻击者可以构造一个"初始是 http,重定向到 file:// 或 ftp://"的响应链,诱导 curl 去访问本不该访问的资源。重定向目标协议必须受到比普通传输更严格的限制。
6.2--proto-redir:单独约束重定向协议
curl 为此提供--proto-redir(7.21.0 起),它只约束重定向跳转时允许的协议,语法与--proto完全一致。官方示例:
curl --proto-redir -all,http,https --follow http://example.com该命令表示:重定向时只允许 http 和 https,其余一律拒绝。其默认策略是:重定向仅允许 HTTP、HTTPS、FTP、FTPS(自 7.65.2 起收紧),而--proto-redir all或+all会放开全部协议,官方文档明确警告这对安全不利。
6.3--proto与--proto-redir的优先级
优先级规则是:--proto中拒绝的协议,--proto-redir无权放行。也就是说--proto是全局限定,--proto-redir是在其基础上对"重定向场景"再收紧一层。这也解释了为何默认的重定向白名单(http/https/ftp/ftps)会比普通传输的默认集合更窄。
6.4 推荐组合
一个稳妥的"仅 HTTP(S) 传输 + 安全重定向"组合可以写成:
curl --proto =http,https --proto-redir =http,https -L https://example.com--proto =http,https:本次传输只允许 http/https(file、ftp、smb 等一律拒绝);--proto-redir =http,https:重定向同样只允许 http/https,封死协议降级攻击。
配合 docs/cmdline-opts/follow.md 中的--max-redirs还可以进一步限制重定向跳数。
七、无 scheme URL 与--proto-default
--proto只管"协议是否被允许",而当 URL缺少 scheme(如直接写ftp.example.com)时,由--proto-default(7.45.0 起)决定 curl 默认按什么协议猜测。相关要点(见 docs/cmdline-opts/proto-default.md):
- 协议名不区分大小写,且不带
://后缀; - 指定了未知或不支持的协议会触发
CURLE_UNSUPPORTED_PROTOCOL错误; - 它不改变代理的默认协议(代理默认仍是 http);
- 未设置时,curl 按
--url的规则根据主机名猜测协议; - 默认协议不能设为
ipfs或ipns,这两个 scheme 必须显式写在 URL 中。
典型用法:
curl --proto-default https ftp.example.com等价于请求https://ftp.example.com。--proto的许可集合对该默认协议同样生效,二者可配合使用。
八、设计意图总结:为什么--proto对脚本如此友好
综合官方文档与源码实现,--proto的设计有三个值得称道之处:
- 宽容的禁用语义:
--proto -xxx禁用未构建协议只警告不报错(见 3.2 节),保证跨构建环境脚本行为一致,这正是文档中 "This allows scripts to safely rely on being able to disable potentially dangerous protocols" 的含义; - 声明式集合运算:
+/-/=三种修饰符与all关键字组成了一套完整的集合加减语义,可在命令行上精确表达"默认全开、减去若干"或"只保留若干"两种策略; - 重定向独立管控:
--proto-redir与--proto分层配合(tests/data/test1245 验证了拒绝优先),让"正常传输放开、重定向收紧"的安全基线成为可能。
九、附:--proto速查表
| 表达式 | 效果 |
|---|---|
--proto +http或--proto http | 在默认集合上追加许可 http |
--proto -http | 从默认集合中移除 http |
--proto =http | 只允许 http |
--proto -all | 禁止一切协议(请求必然失败,退出码 2) |
--proto -all,https,+http | 只允许 http 和 https |
--proto =http,https | 同上,等价写法 |
--proto -ftp,-smb | 默认集合减去 ftp 和 smb |
多次使用--proto a --proto b | 等价于--proto a,b |
结合 docs/cmdline-opts/proto.md、docs/cmdline-opts/proto-redir.md 与 docs/cmdline-opts/proto-default.md 三份官方选项文档,以及上文引用的源码与测试用例,你可以在自己的脚本和自动化流程中稳定地实现"协议最小化"策略,将传输面收敛到真正需要的协议上。
【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考