跳到主要内容
ecdsa.com
curl (OpenSSL 3 backend)curl

错误信息

curl: (56) OpenSSL SSL_read: error:0A000126:SSL routines::unexpected eof while reading, errno 0

也可能输出为

  • curl: (56) OpenSSL SSL_read: OpenSSL/3.5.7: error:0A000126:SSL routines::unexpected eof while reading, errno 0

较新的 curl 版本会在“SSL_read: ”之后插入 TLS 库的版本(例如“OpenSSL/3.5.7: ”)。OpenSSL 1.1.1 不会这样报告这种情况;在 1.1.1 上,同样的截断通常显示为“curl: (18) transfer closed with N bytes remaining to read”。

curl (OpenSSL 3 backend) — CURLE_RECV_ERROR, SSL_R_UNEXPECTED_EOF_WHILE_READING

含义

TLS 连接已经建立,数据也在传输,随后对端在 curl 仍在等待更多数据时关闭了 TCP 连接,事先没有发送 TLS close_notify。OpenSSL 3 把这种突然的结束报告为错误,而不是正常的流结束,因为缺少 close_notify 正是截断攻击的表现。curl 将其报告为接收失败(退出码 56)。

原因

修复方法

  1. 1.

    找出是哪一跳关闭了连接

    记下出错前传输了多少字节、经过了多少秒,然后直接向后端重发该请求。如果连接恰好在整数秒处被切断,说明是代理或负载均衡器中的超时。

    bash
    # How much arrived, and for how long, before the connection died?
    curl -sv --trace-time https://api.example.com/export.csv -o out.csv 2>&1 | tail -20
    # Same request directly to the backend, bypassing the proxy / load balancer
    curl -sv --resolve api.example.com:443:10.0.0.12 https://api.example.com/export.csv -o out.csv
  2. 2.

    调大被触发的超时限制

    在切断连接的组件中,把限制调到高于最长的正常响应时间;或者让慢端点更快地响应(对大型导出使用流式传输或分页)。

    text
    # nginx in front of a slow upstream (both default to 60s)
    proxy_read_timeout 300s;
    proxy_send_timeout 300s;
    # Load balancers: set the idle timeout above the longest legitimate response time
  3. 3.

    重试幂等传输

    对于经由不稳定链路的下载,让 curl 重试并断点续传。这只是在排查服务端问题期间,在客户端缓解症状。

    bash
    # For idempotent downloads: retry, and resume where the transfer stopped
    curl --retry 5 --retry-all-errors -C - -o big.iso https://downloads.example.com/big.iso
  4. 4.

    服务端代码:正确关闭 TLS 连接

    关闭套接字之前先发送 close_notify(OpenSSL 中为 SSL_shutdown,Python 中为 unwrap(),Go 中为 tls.Conn 的 Close()),并用 Content-Length 或分块编码界定 HTTP 消息体,使正常结束不会产生歧义。

    python
    # Server code: shut TLS down cleanly so the peer receives close_notify
    conn.unwrap()   # Python ssl.SSLSocket: sends close_notify before closing
    conn.close()

相关错误

浏览完整的签名错误库