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
本篇指南围绕 curl 命令行工具的--proto选项展开,讲解如何通过+、-、=三种修饰符,以从左到右的顺序表达式精准限制允许使用的传输协议,并延伸介绍配套的--proto-redir(重定向协议限制)与--proto-default(缺失 scheme 时的默认协议)。读者读完将掌握协议白名单的完整语法、安全最佳实践,并能理解该选项从命令行解析到 libcurl 底层位掩码的全链路实现原理。
--proto是什么
curl 是一个支持 DICT、FILE、FTP(S)、GOPHER(S)、HTTP(S)、IMAP(S)、LDAP(S)、MQTT、POP3(S)、RTSP、SCP、SFTP、SMB(S)、SMTP(S)、TELNET、TFTP、WS(S) 等众多协议的命令行传输工具。协议越多,攻击面越大:一个意外构造的 URL 可能让 curl 尝试访问你并不打算使用的协议(例如从 HTTP 页面跳转到file://或smb://)。--proto正是用来对本次传输“允许使用哪些协议”做白名单收口的关键选项。
该选项的元信息(见 docs/cmdline-opts/proto.md)如下:
- 长选项名:
--proto - 参数:
<protocols>(协议列表) - 功能:Enable/disable PROTOCOLS(启用/禁用协议)
- 引入版本:7.21.0
- 分类:connection curl
- 重复使用:仅一次(Multi: single,但多个协议可写在一个逗号分隔列表中)
--proto的值由逗号分隔的若干“协议条目”组成,每个条目是一个协议名或特殊关键字all,前面可以加一个修饰符。整条规则从左到右顺序求值,后出现的条目基于前一个条目的结果继续修改,这与很多配置文件的“覆盖式”语义不同,理解这一点是用好它的前提。
三个修饰符:+、-、=
根据 docs/cmdline-opts/proto.md 的定义,每个协议条目可带三种修饰符:
| 修饰符 | 语义 | 说明 |
|---|---|---|
+ | 允许 | 在已允许的协议集合中追加该协议;不加修饰符时的默认行为就是+ |
- | 拒绝 | 从已允许的协议集合中移除该协议 |
= | 仅允许 | 忽略此前已允许的协议集合,只允许该协议;但后续条目仍可继续修改结果 |
三个修饰符的语义可以概括为:+是“加白名单项”,-是“减白名单项”,=是“重置为只含此项”。由于规则从左到右求值,=之后出现的-或+依然有效。
原文档中的三个示例
文档给出了三个可以立即上手的例子:
# 使用默认协议集合,但禁用 ftps curl --proto -ftps https://example.com # 只启用 http 和 https(写法一:先用 -all 清空,再用 +http 追加) curl --proto -all,https,+http $URL # 只启用 http 和 https(写法二:直接用 =http,https 重置) curl --proto =http,https $URL第一个例子说明-是在默认集合上做减法;第二、三个例子殊途同归——-all配合+与直接使用=,最终都只放行 http 和 https。注意-all本身是一个完整的协议条目(修饰符-+ 关键字all)。
all关键字与空集合
all表示“所有协议”,可搭配修饰符使用:
-all:清空允许集合(从“全部允许”变为“全部拒绝”),等价于一个空的协议集合;+all或=all:允许全部协议。
若你的 curl 构建中某个协议未被编译进去,或协议名拼写有误,--proto会输出一条警告(unrecognized protocol),而不是直接失败。这一设计让脚本可以放心地“尝试禁用危险的协议”——即使该协议在构建时根本不存在,也不会因为报错而中断脚本执行(详见 src/tool_paramhlp.c 中未知协议的处理逻辑)。
重复使用选项
--proto可以在一条命令行中重复出现多次,效果等同于把所有条目拼接成一个逗号分隔列表。例如:
# 下面两条命令等价 curl --proto =http,https --proto -https $URL curl --proto =http,https,-https $URL协议名参考:curl 支持哪些协议
--proto中的协议名与 URL scheme 对应,大小写不敏感。仓库中的 docs/cmdline-opts/_PROTOCOLS.md 系统列出了 curl 支持的协议及其用途,整理如下:
| 协议 | 用途 |
|---|---|
dict | 使用在线词典查词 |
file | 读写本地文件(不支持远程file://;Windows 下原生 UNC 方式可用,仅绝对路径) |
ftp/ftps | 文件传输协议,可选 TLS 加密 |
gopher/gophers | 获取文件 |
http/https | HTTP 协议,依据构建选项与命令行参数可讲 0.9、1.0、1.1、2、3 版本 |
imap/imaps | 邮件读取协议,可下载邮件,可选 TLS |
ldap/ldaps | 目录查询,可选 TLS |
mqtt | MQTT 3 版;下载即订阅主题,上传即发布主题(MQTT over TLS 尚未支持) |
pop3/pop3s | 从 POP3 服务器下载邮件,可选 TLS |
rtsp | RTSP 1.0 下载 |
scp | SSH 2 版 scp 传输 |
sftp | 基于 SSH 2 的 SFTP(draft 5) |
smb/smbs | SMB 1 版上传与下载 |
smtp/smtps | 向 SMTP 服务器上传内容即发送邮件,可选 TLS |
telnet | 交互式会话,把 stdin 内容发给服务器并回显服务器输出 |
tftp | TFTP 下载与上传 |
ws/wss | 基于 HTTP/1 的 WebSocket,wss意味着走 HTTPS |
需要强调的是:你本机 curl 构建实际支持的协议集合不一定包含全部。用curl --version输出中的Protocols:行可以查看当前构建真正内置了哪些协议(见 src/tool_help.c 的实现,该列表来自 libcurl 运行时的curl_version_info()返回的协议数组,见 src/tool_libinfo.c)。
安全实践:为什么以及怎么限制协议
在自动化脚本、定时任务或任何处理不可信 URL 的场景中,建议用--proto把允许集合收紧到业务必需的最小集,通常就是:
# 脚本只允许 HTTP/HTTPS curl --proto =http,https "$URL" # 需要允许重定向跟随时,配合 --proto-redir 双保险(见下文) curl --proto =http,https --proto-redir =http,https --location "$URL"这样做的好处是:即使 URL 被篡改成了file:///etc/passwd、smb://...或ftp://...,curl 也会直接拒绝发起对应协议的请求,从源头阻断一类协议走私(protocol smuggling)与 SSRF 风险。
配套选项:--proto-redir与--proto-default
--proto只约束初始请求的协议,不会自动约束跟随重定向(--location)时跳转过去的协议。为此 curl 提供了两个紧密相关的选项,它们与原文档See-also一节相互引用。
--proto-redir:限制重定向协议
见 docs/cmdline-opts/proto-redir.md。语法与--proto完全相同(同样的逗号分隔、修饰符、all关键字),但作用于重定向目标 URL。关键行为:
--proto中明确拒绝的协议不会被--proto-redir重新放行——两条规则取交集,--proto的约束优先级更高;- 默认情况下(7.65.2 起)重定向只允许HTTP、HTTPS、FTP、FTPS;
- 显式写出
all或+all会放开全部协议,官方文档明确警告这对安全不利。
官方示例:
# 只允许 http 和 https 作为重定向目标 curl --proto-redir -all,http,https --follow http://example.com # 更推荐的写法:用 = 直接重置 curl --proto-redir =http,https --location http://example.com--proto-default:为缺失 scheme 的 URL 指定默认协议
见 docs/cmdline-opts/proto-default.md。当 URL 没有写协议前缀时,curl 默认会根据主机名猜测(例如不加协议时通常猜 http),而--proto-default <protocol>可以显式指定:
curl --proto-default https ftp.example.com # 把 ftp.example.com 当作 https://ftp.example.com注意事项(均为文档原述):
- 协议名大小写不敏感;若指定了未知或不支持的协议,会得到
CURLE_UNSUPPORTED_PROTOCOL错误; - 该选项不改变默认代理协议(代理仍默认走 http);
- 默认协议不能设置为
ipfs或ipns,这两个 scheme 必须显式写在 URL 中。
源码级原理:从命令行到 libcurl 位掩码
--proto的完整链路可以用仓库源码逐层还原,这有助于理解其行为边界。
第一层:参数解析与集合运算
curl 工具在启动时调用get_libcurl_info(),通过curl_version_info()拿到当前 libcurl 实际支持的协议名数组,并记录协议数量(src/tool_libinfo.c)。随后proto_token()把协议名字符串“归一化”为指向该数组内部字符串的稳定指针,从而可以用指针比较代替字符串比较(src/tool_libinfo.c)。
--proto的参数解析入口位于 src/tool_getparam.c,调用proto2num()(实现在 src/tool_paramhlp.c)。proto2num的核心逻辑如下:
- 用默认协议集合初始化一个集合数组(
protoset); - 逐个解析逗号分隔的条目,根据首字符识别修饰符:
=对应set动作、-对应deny、+或无修饰符对应allow; - 遇到
all:deny时清空集合(protoset[0] = NULL),allow/set时把built_in_protos整体拷入集合; - 遇到具体协议名:
deny从集合移除、set先清空再放入、allow直接放入;未知协议会打印unrecognized protocol并返回PARAM_BAD_USE; - 解析完成后按字母序排序,保证输出稳定(源码注释明确说明这是为满足 CI 测试的稳定输出要求)。
注意proto2num中set(=)动作的实现:protoset[0] = NULL之后以FALLTHROUGH落到allow分支再放入协议——这正是“=重置集合、后续条目可继续修改”这一文档语义的代码级证据。
第二层:传递到 libcurl
解析得到的协议字符串存入config->proto_str(src/tool_cfgable.h)。在 src/config2setopts.c 中,proto_str被映射为CURLOPT_PROTOCOLS_STR,proto_redir_str被映射为CURLOPT_REDIR_PROTOCOLS_STR,交给 libcurl 处理。
第三层:libcurl 内部位掩码
在 libcurl 的选项处理中(lib/setopt.c):
CURLOPT_PROTOCOLS_STR:通过protocol2num()把字符串转成curl_prot_t位掩码存入s->allowed_protocols;传入NULL时重置为默认值CURLPROTO_64ALL(即全部协议);CURLOPT_REDIR_PROTOCOLS_STR:同理存入s->redir_protocols,默认值为CURLPROTO_REDIR——这与前文“重定向默认只允许 HTTP/HTTPS/FTP/FTPS”的文档描述对应。
传输执行时,libcurl 会先校验目标 URL 的协议是否在allowed_protocols位掩码内,重定向场景则校验redir_protocols,不在白名单内即拒绝发起连接,从而把限制落到实处。
与编程 API 的对应
--proto系列在 libcurl 编程接口中也有等价物,方便你在自己的程序里实现同样的约束:
| curl 命令行 | libcurl 选项 |
|---|---|
--proto | CURLOPT_PROTOCOLS/CURLOPT_PROTOCOLS_STR |
--proto-redir | CURLOPT_REDIR_PROTOCOLS/CURLOPT_REDIR_PROTOCOLS_STR |
--proto-default | CURLOPT_DEFAULT_PROTOCOL |
(字符串版本选项的 setopt 处理均位于 lib/setopt.c。)
常见用法速查与注意事项
# 只允许 http、https(最常用,适合脚本与 SSRF 防护) curl --proto =http,https $URL # 默认集合减去某个协议 curl --proto -ftps $URL # 只允许 http、https 且重定向也受限 curl --proto =http,https --proto-redir =http,https -L $URL # 缺失 scheme 时按 https 处理 curl --proto-default https host.example.com # 组合使用:先清空再逐项添加 curl --proto -all,https,+http $URL要点回顾:
- 顺序敏感:规则从左到右求值,
=之后仍可用+/-微调; - 大小写不敏感:协议名可写
http或HTTP; - 未知协议只警告不失败:便于脚本安全地禁用未构建的协议;
--proto与--proto-redir取交集:前者拒绝的协议不会被后者放行;- 默认行为:不指定
--proto时允许全部已构建协议;不指定--proto-redir时重定向仅允许 HTTP/HTTPS/FTP/FTPS(7.65.2 起)。
--proto的完整权威定义位于仓库 docs/cmdline-opts/proto.md,配套协议清单见 docs/cmdline-opts/_PROTOCOLS.md,参数解析实现见 src/tool_paramhlp.c,libcurl 侧位掩码处理见 lib/setopt.c。需要更细粒度控制时,可将这三者与--proto-redir、--proto-default组合使用,构建一套完整的协议级访问控制策略。
【免费下载链接】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),仅供参考