ssl-bad-ecpoint-经验帖

Python requests 遇到 BAD_ECPOINT 的终极解决方案

一句话总结

SSLError(1, '[SSL: BAD_ECPOINT] bad ecpoint') 发生在 TLS 握手层 ,与证书验证无关,verify=False 无效。根因是目标服务器使用 ECDH 压缩曲线,OpenSSL 1.1+ 拒绝握手。Windows schannel 无此限制所以本地正常。最终方案:用 curl_cffi 替代 requests,绕过 Python _ssl 模块。

问题场景

湖南生态环境厅网站 sthjt.hunan.gov.cn,在 Docker 容器(python:3.12-slim-bullseye,OpenSSL 1.1)中请求时直接报错:

复制代码
requests.exceptions.SSLError: HTTPSConnectionPool(host='sthjt.hunan.gov.cn', port=443):
Max retries exceeded (Caused by SSLError(SSLError(1, '[SSL: BAD_ECPOINT] bad ecpoint (_ssl.c:1000)')))

Windows 本地运行完全正常

根因分析

为什么 verify=False 无效?

SSL/TLS 握手分为两层:

复制代码
┌─────────────────────────────────┐
│  证书验证层 (verify=True/False)   │  ← verify=False 只跳过这一层
├─────────────────────────────────┤
│  TLS 握手层 (密钥交换/加密套件)    │  ← BAD_ECPOINT 发生在这里
└─────────────────────────────────┘

BAD_ECPOINT 发生在 TLS 握手阶段的 ECDH 密钥交换环节------服务器返回了压缩格式的椭圆曲线点(compressed EC point),OpenSSL 1.1.0+ 出于安全考虑拒绝接受。这是握手层面的拒绝,比证书验证更底层。

为什么 Windows 正常,Linux 报错?

Python 的 SSL/TLS 能力来自 CPython 编译时链接的 SSL 后端:

平台 SSL 后端 对压缩 EC 点的态度
Windows schannel(系统自带) 容忍
Linux (OpenSSL 1.1+) OpenSSL 拒绝
Linux (OpenSSL 3.x) OpenSSL 拒绝
macOS Secure Transport 容忍

关键点 :Python 的 _ssl 模块是 C 扩展,在 CPython 编译时就链接好了,运行时无法替换apt install opensslpip install pyOpenSSL 都改变不了 _ssl 的行为。

尝试过的无效方案

按时间顺序,以下方案均失败

方案 结果 原因
verify_ssl = False 错误在握手层,不在证书层
_LegacySSLAdapter + SECLEVEL=1 安全级别与 EC 点压缩是正交问题
_LegacySSLAdapter + SECLEVEL=0 + OP_LEGACY_SERVER_CONNECT 同上,OpenSSL 1.1 根本不允许压缩 EC 点
降级到 python:3.12-slim-bullseye(OpenSSL 1.1) OpenSSL 1.1.0 开始就拒绝压缩 EC 点了
云端浏览器 API 代理 API 服务端自身崩溃(api_server.py 内部报错)

最终方案:curl_cffi

原理

curl_cffi 通过 CFFI 直接调用 libcurl,完全不经过 Python 的 _ssl 模块 。TLS 握手由 curl 自己的 SSL 后端处理,从根本上规避了 Python _ssl 的限制。

此外它还支持 TLS 指纹伪装(模拟 Chrome/Firefox/Safari 的 JA3 指纹),顺带解决基于 TLS 指纹的反爬。

安装

bash 复制代码
pip install curl_cffi

支持 Python 3.10+,有预编译的 manylinux/musllinux wheel,无需编译。

代码改动

改动量极小------curl_cffi 的 API 与 requests 完全兼容:

python 复制代码
# 之前
import requests
session = requests.Session()

# 之后
from curl_cffi import requests
session = requests.Session(impersonate="chrome124")  # 模拟 Chrome 124 TLS 指纹

完整代码(湖南 pipeline 实际改动):

python 复制代码
from curl_cffi import requests

class HunanPipeline(BasePipeline):
    province = "湖南"

    def _create_session(self) -> requests.Session:
        """使用 curl_cffi Session(Chrome 指纹 + libcurl TLS,绕过 BAD_ECPOINT)"""
        session = requests.Session(impersonate="chrome124")
        session.headers.update({
            "User-Agent": "Mozilla/5.0 ... Chrome/124.0.0.0 Safari/537.36",
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "Accept-Language": "zh-CN,zh;q=0.9",
        })
        return session

    def collect(self, session, year_filter, first_page_only=False):
        # ... session.get(url) 用法完全不变
        resp = session.get(url, timeout=30)

    def process_item(self, session, item):
        # ... 同样不变
        resp = session.get(item["url"], timeout=30)

不需要 verify_ssl = False不需要 _LegacySSLAdapter不需要改 Dockerfile 基础镜像。

注意事项

  1. curl_cffi 的异常类继承自自己的 curl_cffi.requests.errors.RequestsError不是 标准库的 requests.RequestException。如果基类用 except requests.RequestException 做重试,需要改为同时捕获两者的基类,或用 except Exception 兜底。
  2. impersonate 参数可选的浏览器版本:chrome110chrome120chrome124firefoxsafari 等。国内政府网站建议用 chrome124
  3. 详情页的超时时间建议 > 30s(政府网站响应可能较慢)。

推广适用场景

这个方案不仅适用于 BAD_ECPOINT,同样的思路可以解决其他 Python _ssl 层面的问题:

  • SSLError: unsupported protocol --- 服务器仅支持老旧 TLS 版本
  • SSLError: dh key too small --- DH 密钥长度不足
  • SSLError: no shared cipher --- 加密套件不兼容
  • TLS 指纹反爬(JA3 检测)

只要问题根因在 Python _ssl 模块层面,且无法通过升级/配置 OpenSSL 解决,就可以考虑用 curl_cffi 绕开。

参考

相关推荐
小洁忘了怎么分身7 小时前
多样本空间转录组 Harmony 整合与标签转移指南
网络·r语言·生信分析
发光小北8 小时前
PLC 私有协议采集网关如何应用?
网络协议
三言老师9 小时前
CentOS8 Firewalld 高级规则中级实操教程
linux·运维·服务器·网络·centos
rcms1527026921810 小时前
TEL 3308-000065-13 印刷电路板
网络
xywww16811 小时前
Claude Opus 5 企业使用怎么选:场景分层、成本测算和接入风险清单
网络
望安认证11 小时前
CRA 与 CE、RED、EUCC、NIS2、GDPR、AI Act、Data Act、DORA 等欧盟法规的关系:从法案结构看数字产品出海合规
网络·欧盟·cra·网络弹性法案·cra认证
Hrain-AI12 小时前
2026企业AI Agent本地化落地:6平台横评+搭建步骤+成本模板
服务器·网络·人工智能
便利店102412 小时前
企业远程办公如何打通内网?网络层隧道与封装直觉
运维·服务器·网络·vpn·隧道
00后程序员张13 小时前
抓包鹰 电脑本机网络连接表怎么看?哪些程序在联网 进程归属与可疑 IP 追溯
网络协议·计算机网络·网络安全·ios·adb·https·udp
砚凝霜13 小时前
软考网络工程师|第 6 章 网络安全基础、攻击、等保完整备考笔记
网络·笔记·web安全