curl 库 CURLOPT_DOH_SSL_VERIFYHOST 详解:DoH 服务器证书主机名校验的安全开关
2026/9/24 6:18:14 网站建设 项目流程

curl 库 CURLOPT_DOH_SSL_VERIFYHOST 详解:DoH 服务器证书主机名校验的安全开关

【免费下载链接】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

本指南围绕 libcurl 的CURLOPT_DOH_SSL_VERIFYHOST选项展开,讲解它在 DNS-over-HTTPS(DoH)请求中如何校验 DoH 服务器证书的主机名,并给出默认值、取值语义、命令行对应参数与底层源码实现。读完你将掌握:何时需要关闭 DoH 主机名校验、关闭后带来的安全风险,以及该选项与CURLOPT_SSL_VERIFYHOSTCURLOPT_DOH_SSL_VERIFYPEER之间的区别与配合方式。

选项概览

CURLOPT_DOH_SSL_VERIFYHOST用于控制 libcurl 是否校验 DoH(DNS-over-HTTPS)服务器 SSL 证书中的主机名字段。它只作用于发往 DoH 服务器的请求,是 CURLOPT_SSL_VERIFYHOST 在 DoH 场景下的对应物("DoH equivalent")。

#include <curl/curl.h> CURLcode curl_easy_setopt(CURL *handle, CURLOPT_DOH_SSL_VERIFYHOST, long verify);
  • 选项类型:long,取值只能为 0、1 或 2。
  • 协议:TLS(仅影响 HTTPS 形式的 DoH 请求)。
  • 适用 TLS 后端:全部(OpenSSL、GnuTLS、Schannel、wolfSSL、mbedTLS 等)。
  • 加入版本:7.76.0(Added-in: 7.76.0)。

在 libcurl 的选项注册表中,该选项被声明为CURLOT_LONG类型(见 lib/easyoptions.c),即它接受一个长整型数值而非布尔开关。

取值语义:2、1、0 分别代表什么

设为 2(推荐):严格校验主机名

传入2L表示要求 curl校验DoH 服务器证书中的名称字段是否与主机名匹配。此时 DoH 服务器提供的 SSL 证书必须表明服务器名与你打算连接的服务名一致,否则连接失败。

curl 判定 DoH 服务器为"预期服务器"(intended one)的条件是:证书中的Common Name(CN)字段Subject Alternative Name(SAN)字段与你在 DoH URL 中告诉 curl 连接的主机名相匹配。

注意:现代 CA 签发的证书普遍使用 SAN 扩展承载主机名,CN 字段常为空或仅为组织名,因此 SAN 匹配是实际校验的关键路径。

设为 1:与 2 等效,但不建议

当值设为1L时,其行为与2L完全相同("treated the same as 2L")。但文档明确建议:为了与其他*_VERIFYHOST选项保持一致,应使用 2 而不是 1。

设为 0:完全跳过主机名校验

当值设为0L时,无论证书中使用什么名称,连接都会成功("the connection succeeds regardless of the names used in the certificate")。文档特别警告:请谨慎使用这一能力("Use that ability with caution")。

从源码看,02是两个真正有差异的状态,而1被归一化为2的行为:在 lib/vdns/doh.c 中,libcurl 内部创建 DoH 探测用的临时 easy handle 时,是这样映射的:

if(maybe_https) { ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, >#ifndef CURL_DISABLE_DOH set->doh_verifyhost = TRUE; set->doh_verifypeer = TRUE; #endif

Curl_init_userdefined在创建 easy handle 时把doh_verifyhost初始化为TRUE,结合上面TRUE ? 2L : 0L的映射,等价于默认值 2。同类项doh_verifypeer(对应CURLOPT_DOH_SSL_VERIFYPEER)默认也是开启的,因此在默认配置下,DoH 请求的证书既要做签名验证,也要做主机名校验。

与相关选项的搭配关系

选项作用默认值
CURLOPT_DOH_SSL_VERIFYHOST校验 DoH 服务器证书的主机名2
CURLOPT_DOH_SSL_VERIFYPEER校验 DoH 服务器证书的数字签名(证书链真实性)1
CURLOPT_SSL_VERIFYHOST校验目标站点(如https://example.com)证书主机名2
CURLOPT_SSL_VERIFYPEER校验目标站点证书签名1
CURLOPT_PROXY_SSL_VERIFYHOST校验 HTTPS 代理证书主机名2
CURLOPT_PROXY_SSL_VERIFYPEER校验 HTTPS 代理证书签名1

三者(目标站点、DoH 服务器、代理)的主机名校验彼此独立,分别由各自的VERIFYHOST选项控制。文档中See-also列表也明确指向了上述这些兄弟选项。

另外从实现注释看(lib/vdns/doh.c),DoH 请求不会继承用户传输会话的代理服务器设置,因此代理相关的 SSL 校验选项对 DoH 连接不生效,也就无需担心CURLOPT_PROXY_SSL_VERIFYHOST影响 DoH。

完整示例:显式关闭 DoH 主机名校验

以下代码来自选项文档的官方示例(EXAMPLE小节),演示了如何为 DoH 服务器关闭主机名校验:

int main(void) { CURL *curl = curl_easy_init(); if(curl) { CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); curl_easy_setopt(curl, CURLOPT_DOH_URL, "https://cloudflare-dns.com/dns-query"); /* Disable hostname verification of the DoH server */ curl_easy_setopt(curl, CURLOPT_DOH_SSL_VERIFYHOST, 0L); result = curl_easy_perform(curl); curl_easy_cleanup(curl); } }

要点解读:

  1. CURLOPT_DOH_URL指定 DoH 解析服务器(这里使用 Cloudflare 公共 DoH 端点cloudflare-dns.com/dns-query),该选项对应的内部字符串槽位为STRING_DOH(见 lib/urldata.h 与 lib/setopt.c)。
  2. CURLOPT_DOH_SSL_VERIFYHOST设为0L后,curl 在解析example.com的主机名时,不再校验 DoH 服务器证书的主机名。
  3. CURLOPT_SSL_VERIFYHOST未被改动,因此目标站点example.com本身的证书主机名校验仍然保持默认的严格模式(2),两者的影响范围互不干扰。

命令行对应:curl 工具的 --doh-insecure

curl 命令行工具提供了对应的高级开关--doh-insecure,它同时关闭 DoH 连接的证书对端校验(peer)与主机名校验(host)。相关实现位于 src/tool_getparam.c:

case C_DOH_INSECURE: /* --doh-insecure */ config->doh_insecure_ok = toggle;

当该标志置位后,工具在 src/config2setopts.c 中会向 libcurl 写入对应的 DoH 校验关闭选项,最终作用于底层doh_verifyhost/doh_verifypeer两个位标志。也就是说,--doh-insecure等价于在代码中同时设置CURLOPT_DOH_SSL_VERIFYHOST = 0LCURLOPT_DOH_SSL_VERIFYPEER = 0L

典型用法:

curl --doh-url https://cloudflare-dns.com/dns-query --doh-insecure https://example.com

底层实现原理:DoH 探测请求如何被构造

理解该选项背后的机制,需要了解 libcurl 的 DoH 实现。DoH 解析逻辑集中在 lib/vdns/doh.c,入口为Curl_doh()(声明见 lib/vdns/doh.h),它在Curl_doh_wanted()判定用户设置了 DoH 后被调用(见 lib/vdns/hostip.c)。

关键流程(对应 lib/vdns/doh.c):

  1. 编码 DNS 查询doh_req_encode()把要解析的主机名与 DNS 类型(A/AAAA)编码为 wire format 的 DNS 报文,存入req_body
  2. 创建内部 easy handle:调用Curl_open()(内部版curl_easy_init())创建一个临时Curl_easy *doh,专门用于向 DoH 服务器发送 HTTP POST 请求。
  3. 组装 HTTP 请求:设置CURLOPT_URL为 DoH URL、CURLOPT_POSTFIELDS为 DNS 报文,并附加Content-Type: application/dns-message头(lib/vdns/doh.c);在 HTTPS 场景下默认协商 HTTP/2(CURL_HTTP_VERSION_2TLS)。
  4. 强制 HTTPS:非调试构建下强制只允许 HTTPS 协议访问 DoH(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS,见 lib/vdns/doh.c),避免 DNS 查询内容以明文传输。
  5. 继承校验设置并映射选项:即上文展示的doh_verifyhost ? 2L : 0L映射,把用户的 DoH 校验偏好翻译为内部 handle 的CURLOPT_SSL_VERIFYHOST;同时仅继承 CA 相关设置(CURLOPT_CAINFOCURLOPT_CAINFO_BLOBCURLOPT_CAPATH等),并显式注释说明不继承代理 SSL 设置
  6. 回调接收响应doh_probe_write_cb()(lib/vdns/doh.c)把服务器返回的 DNS 响应报文累积到resp_body,随后由Curl_doh_take_result()解析出最终 IP 地址。

因此,CURLOPT_DOH_SSL_VERIFYHOST的真正作用点,是在第 5 步决定内部 DoH handle 的 TLS 主机名校验强度。当它取默认值 2 时,若 DoH 服务器证书的 CN/SAN 与 DoH URL 中的主机名不匹配,TLS 握手会在校验阶段失败,进而导致 DNS 解析失败——这正是防止 DoH 流量被中间人劫持到伪造服务器上的核心保障。

安全建议与使用注意

  • 保持默认值 2:DoH 本身用于对抗 DNS 劫持/污染,若同时关闭证书主机名校验,攻击者只需伪造一个名称不匹配的证书即可截获你的 DNS 查询内容(包括查询域名本身),安全性将大打折扣。
  • 0 值仅用于调试:仅在自建 DoH 服务、自签名证书、测试环境或临时排查问题时使用0L,并配合CURLOPT_DOH_SSL_VERIFYPEER = 0L使用;生产环境不要关闭。
  • 1 值不要使用:虽然行为与 2 相同,但为了代码可读性与一致性,一律写2L
  • 不影响目标站点校验:本选项只作用于 DoH 服务器,https://example.com自身的证书校验仍由CURLOPT_SSL_VERIFYHOST控制,两者独立配置、互不干扰。
  • 构建前提:该选项在CURL_DISABLE_DOH编译宏开启的构建中不可用(lib/setopt.c 中整个分支被#ifndef CURL_DISABLE_DOH保护),使用前可通过curl_version_info()或官方文档确认构建包含 DoH 支持。

返回值

curl_easy_setopt(3)返回CURLcode以指示成功或失败:

  • CURLE_OK(0):选项设置成功;
  • 非零值:发生错误,具体错误码见 libcurl-errors(3)。

注意该返回值只反映"选项是否被接受",与 DoH 证书校验是否通过无关;证书校验失败发生在后续curl_easy_perform()阶段,并以CURLE_PEER_FAILED_VERIFICATION等错误码返回。

参考文档

  • 选项手册:docs/libcurl/opts/CURLOPT_DOH_SSL_VERIFYHOST.md
  • 设置逻辑:lib/setopt.c
  • 默认值初始化:lib/url.c
  • 内部标志定义:lib/urldata.h
  • DoH 探测与选项映射:lib/vdns/doh.c
  • DoH 入口声明:lib/vdns/doh.h
  • 命令行--doh-insecure:src/tool_getparam.c

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询