news 2026/8/31 10:12:07

服务器证书被换了?用curl的公钥钉扎把这道防线焊死

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
服务器证书被换了?用curl的公钥钉扎把这道防线焊死

服务器证书被换了?用curl的公钥钉扎把这道防线焊死

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

HTTPS 默认信任"证书颁发机构",但如果 CA 被攻破或签发系统出问题,一张"合法"的假证书照样能拿到你的数据。curl 提供了一条更硬的底线:证书钉扎——在客户端预先记下服务器公钥的指纹,TLS 握手阶段一旦发现指纹对不上,curl 直接在收发任何数据之前就掐断连接。这条机制对应 libcurl 的CURLOPT_PINNEDPUBLICKEY选项和命令行的--pinnedpubkey参数,自 7.39.0 起可用,目前被 OpenSSL、GnuTLS、wolfSSL、mbedTLS、Schannel 五个 TLS 后端支持。

钉的是公钥,不是证书

先把一个容易混淆的点说清楚:很多人以为钉扎的对象是整张证书。不是。

TLS 握手时服务器会出示证书,curl 从证书里抽出公钥,再和你预存的指纹比对。这样做的好处是解耦:

  • 证书可以到期、续期、换 CA,只要密钥对不变,钉扎就继续有效;
  • 攻击者即使用同一域名骗到一张新证书,只要公钥不是当初那把,照样被拒。

类比一下:证书核验像核对"身份证",身份证会换发、可能被伪造补办;公钥钉扎更像比对"指纹",指纹换不了。

命令行的入口文档在 docs/cmdline-opts/pinnedpubkey.md,库选项的完整说明在 docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md。

三种钉法:一个哈希、一串哈希、一个文件

--pinnedpubkey接受两类输入,用分号还能并列多个:

  1. 单个 SHA256 指纹sha256//前缀加一段 base64 编码的公钥摘要
    curl --pinnedpubkey 'sha256//ce118b51897f4452dc' https://example.com
  2. 多个指纹并列:服务器轮换密钥时,把新旧两把都列上,避免"换钥即断线"
    curl --pinnedpubkey 'sha256//AAA=;sha256//BBB=' https://example.com
  3. 公钥文件:直接传一个 PEM 或 DER 格式的公钥文件路径
    curl --pinnedpubkey /etc/keys/server.pub https://example.com

在程序里用 libcurl,只需多设一个选项:

curl_easy_setopt(curl, CURLOPT_PINNEDPUBLICKEY, "sha256//AAA=;sha256//BBB=");

多个指纹是"任一命中即放行"的关系,所以第 2 种写法是密钥轮换期的标准姿势。

源码里发生了什么

整条校验链的公共入口是Curl_pin_peer_pubkey,位于 lib/vtls/vtls.c,各 TLS 后端在握手完成后各自把证书公钥交进来,然后走同一段逻辑:

步骤做的事不通过时
1判断输入是不是sha256//开头不是则改走文件比对分支
2对服务器公钥算 SHA256,再做 base64 编码后端缺 SHA256 能力直接失败
3逐个切分预置指纹串,做逐字节精确比较(长度+memcmp)全部不中
4任一命中即返回成功返回错误码 90CURLE_SSL_PINNEDPUBKEYNOTMATCH

几个值得注意的工程细节:

  • 走文件分支时,curl 先ftell文件大小,公钥长度大于文件直接判不匹配,不浪费 IO;
  • 摘要比较用的是memcmp,不做模糊容错——指纹错一个字符就是失败;
  • 失败时的错误信息由 lib/strerror.c 统一给出文案,方便上层程序向用户透传。

OpenSSL 后端的调用点在 lib/vtls/openssl.c 的ossl_pkp_pin_peer_pubkey里,GnuTLS、Schannel、mbedTLS、wolfSSL 各自在对应文件中实现同一条契约,接口原型声明在 lib/vtls/vtls.h。

三个最容易踩的坑

坑一:钉扎不依赖证书验证开关。--pinnedpubkey--insecure(即CURLOPT_SSL_VERIFYPEER关闭)互相独立。即使你为了跳过证书校验关掉了验证,公钥比对依然会执行——这是设计出来的"最后防线",而不是被--insecure一起禁用的可选项。

坑二:重定向到新域名时不查。钉扎只校验传输的初始源站。如果请求跟随了跳转到别的域名的重定向,新域名不会拿这把指纹去验。如果你的流程涉及跨域重定向,需要单独评估。

坑三:LDAP 有个例外。使用传统 LDAP 后端时,TLS 不走 curl 自己的 TLS 层,钉扎对它无效;用 OpenLDAP 后端编译的 libcurl 才会走统一层并生效。

另外记一下后端支持的时间线,排查"为什么我这版本不生效"时很有用:

  • PEM/DER 文件:7.39.0(OpenSSL、GnuTLS)→ 7.43.0(wolfSSL)→ 7.47.0(mbedTLS)→ 7.58.1(Schannel)
  • sha256 指纹:7.44.0(OpenSSL、GnuTLS、wolfSSL)→ 7.47.0 → 7.58.1

手上没有公钥?用 openssl 现算指纹

官方文档给出的最短路径:先拿证书,再抽公钥,转 DER,最后算摘要。一条链子跑完:

# 1. 抓服务器证书(记得肉眼核对是不是预期的那张) openssl s_client -servername www.example.com -connect www.example.com:443 \ < /dev/null | sed -n "/-----BEGIN/,/-----END/p" > www.example.com.pem # 2. 从证书抽出公钥 openssl x509 -in www.example.com.pem -pubkey -noout > server.pubkey.pem # 3. PEM 转 DER openssl asn1parse -noout -inform pem -in server.pubkey.pem -out server.pubkey.der # 4. 算 sha256 并 base64,拼出可粘贴的指纹 openssl dgst -sha256 -binary server.pubkey.der | openssl base64

把第 4 步输出的字符串前面加上sha256//,就是--pinnedpubkey要的值。注意第 1 步"肉眼核对证书"不是客套话——如果抓证书时你正被中间人,后面算出来的指纹本身就是错的。

上线前自查清单

  • 预置当前 + 备用两把指纹,密钥轮换时先加新、后删旧,中间不断连
  • 指纹获取全程在受控网络上执行,并核对证书主体与预期一致
  • 程序捕获错误码 90(CURLE_SSL_PINNEDPUBKEYNOTMATCH)并给用户明确的失败原因,而不是笼统的"网络错误"
  • 跨域重定向、LDAP 旧后端等特殊路径单独评估
  • 把"重新获取指纹"的动作纳入证书/密钥轮换的运维流程,写进 checklist

curl 的公钥钉扎不是替代 CA 校验,而是叠在 CA 校验之上的一道独立检查:证书可以换,公钥必须是你认识的那把。两者同时在线,中间人手里至少要多一把你无法伪造的东西。图示:证书公钥提取与 SHA256 指纹比对流程(服务器出示证书 → 抽取公钥 → 摘要比对 → 命中放行/不命中返回错误码 90)

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/31 10:11:14

Codex CLI 定时任务实战:从 crontab 到自动化工作流

把 Codex CLI 放进定时任务&#xff0c;听起来像是一个极客玩具&#xff0c;但实际用起来之后&#xff0c;它已经成了我每天工作流里不可缺的自动化角色。我现在每天会跑 3 个 Codex 定时任务&#xff1a;早上生成前一天的代码变更摘要&#xff0c;周日晚上生成周报初稿&#x…

作者头像 李华
网站建设 2026/8/31 10:06:16

华硕弘道AI笔记本实战:搭建贷后催收AI工作流指南

“周志”这个词&#xff0c;最初看到时我以为是某位同事的名字&#xff0c;后来才知道这是一个贷后管理项目的代号&#xff0c;也可以理解为“周度业绩日志”的简称。项目并不复杂&#xff0c;但有一个很有代表性的矛盾&#xff1a;流程本身非常成熟&#xff0c;话术模板、客户…

作者头像 李华
网站建设 2026/8/31 10:03:58

硬件面试通关指南:基础、项目复盘与排错技巧全解析

硬件面试不是把课本上的知识点背一遍就能通过的。真实面试里&#xff0c;面试官会围绕你的简历项目、常用接口、电源设计、信号完整性和一次真实的调试经历不断追问&#xff0c;直到确认你是在真正做硬件&#xff0c;而不是只会背结论。很多候选人在笔试环节能拿高分&#xff0…

作者头像 李华
网站建设 2026/8/31 9:58:10

10 分钟跑通 LocalAI:本地部署私有 AI 推理服务的完整指南

10 分钟跑通 LocalAI&#xff1a;本地部署私有 AI 推理服务的完整指南 【免费下载链接】LocalAI LocalAI is the open-source AI engine. Run any model - LLMs, vision, voice, image, video - on any hardware. No GPU required. 项目地址: https://gitcode.com/GitHub_Tre…

作者头像 李华