Python 3.13因SSL验证规则变更导致邮件发送失败,寻求安全替代解决方案
我最近碰到个头疼的问题:之前用Python的email包发邮件在3.12及更早版本都好好的,升级到3.13之后直接失败了。排查下来发现问题不在email包本身,而是SSL验证的规则变了。
具体来说,Python 3.13对SSL证书的要求更严格了——现在它会检查证书里的Basic Constraints字段是否设为Critical,而之前的版本完全忽略这个要求。如果你的邮件服务器证书里这个字段没设成Critical,SSL握手就会验证失败,直接抛出异常。
我的情况是,邮件服务器用的证书这个字段设的是False,而我的托管服务商没法为单个客户修改证书配置,要是给每个域名都买自定义证书成本又太高。当然直接关闭SSL验证是个办法,但这显然不安全,所以想问问有没有其他朋友碰到过这个问题,有没有不用换证书的安全解决办法?
之前运行的代码大概是这样的:
import ssl import smtplib EmailServer = 'SomeDomain.com' EmailPort = 465 context = ssl.create_default_context() with smtplib.SMTP_SSL (EmailServer, EmailPort, context=context) as server: server.login(EmailServerUsername, EmailServerPassword) server.send_message(msg)
在Python 3.13.1里,代码执行到with smtplib.SMTP_SSL这一行就会抛出异常,根本到不了server.login步骤。抛出的错误信息是:
Emailout exception [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Basic Constraints of CA cert not marked critical (_ssl.c:1018)
找到的解决方案
后来我翻了Python 3.13的发布说明,发现是SSL模块新增了一个严格验证的标志,导致证书要求变高了。只要关闭这个STRICT标志,就能回到Python 3.12的验证级别。只需要在创建SSL上下文之后加一行代码:
context.verify_flags &= ~ssl.VERIFY_X509_STRICT
这行代码的作用是把VERIFY_X509_STRICT标志从验证规则里移除,这样就不再要求Basic Constraints字段必须设为Critical,邮件就能正常发送了。
补充一句:之前的标题可能有点不准确,其实不是邮件功能被破坏了,而是Python 3.13提升了SSL安全验证的级别。但如果不用这个标题,可能遇到同样问题的人很难注意到这个变更的影响——毕竟我也是踩坑之后才明白的。不过好在评论区的提示帮我找到了方向,只是可惜我的服务商没法提供符合新要求的证书配置。
备注:内容来源于stack exchange,提问作者Jay Mosk

