Can you expand on what you mean by this? AFAIK Go uses the system X.509 store, but does not use the OS provided TLS impl. Go has its own (written in Go) TLS impl.
Thatâs true and the intended goal (see RFC 8314), but only for MUA communication (client-server), not for MTA communication (server-server).
ESMTPS is what is being most commonly used between mail relay servers (MTAs) nowadays.
Also note that IANA encourages port users who want to add encryption to their protocols to reuse the same port for unencrypted and encrypted versions of their protocols - in order to prevent the continued proliferation of port registrations (a limited resource) due to the desire to use implicit TLS. [1]
As a result, the need to support TLS upgrades of unencrypted connections is not going to go away soon.
Iâm not opposed to having a new TLS module in Python, but would like to see the functionality of the current ssl module (mainly its wrapping feature) to be retained as a functionality in some form.
Itâs also a good idea to try to maintain a compatibility ssl module implementation which then builds on the new TLS module, since thereâs a lot of code out there using the ssl module â including for less commonly known protocols.
BTW: My understanding of the idea behind wrapping existing sockets in OpenSSL is the desire to not have to deal with platform specific network stacks, which removes a lot of complexity from the library.
I was in discussion with IANA about exactly this use case for one of our products and we then agreed to support upgrades on the existing port rather than register a new protocol port for implicit TLS. â©ïž
Oh got it, for some reason I thought they used system TLS instead of writing their own. Iâll update my comment. Thanks Alex!
AFAIK this question hasnât been answered yet (nudge, nudge).
How much needs to be changed here? Dropping a dependency on OpenSSL is nice for the CPython project, but on the other hand relying on OpenSSL does give a consistent developer experience across platforms. Iâm a but worried that relying on platform TLS stack will result in subtle changes in behaviour between platforms that are confusing to developers, although I cannot substantiate this.
An alternative to reviving PEP 543 is to integrate the truststore project into the standard library as the default SSLContext, which would give us integration with the platform trust stores without otherwise changing the experience (he writes wishfully).
Got it, thanks for the clarification. I wasnât aware that MTA traffic encryption was still largely opportunistic, thatâs not ideal but understandable â do you have resources on hand for these numbers? In particular, Iâm curious whether the server-server full TLS issue is mostly due to holdouts from 1-2 large email providers, or whether itâs completely pervasive.
(To take a step back: I think my concerns around socket wrapping a little moot in the context of a âmemory channelâ API, since there will no longer be any socket in that case. With that API we get the best of both worlds: users can perform socket wrapping by operating on the socket before handing it off to the TLS layer, and âfullâ TLS connections can be performed straightforwardly.)
Oh, this may have been poor communication on my part: my thinking here is that these proposed APIs will not immediately replace the ssl
module (since itâs pervasive, like you say), but will instead be introduced over a series of phases:
- New TLS APIs are merged, under a new module or namespace (Iâll call this
tlslib
purely for example purposes) - CPythonâs internal uses of
ssl
are replaced withtlslib
over time, external users begin to transition totlslib
as well ssl
is marked as deprecated, similar to theoptparse
/argparse
transition in 3.2ssl
is turned into_ssl
or similar (probably not_ssl
itself since thatâs the native extension) to mark it as an internal implementation concernssl
(in its private form) is removed outright
My thinking is that this process would probably take (many) years, which should give external users time to perform deprecations and migrations. And of course this doesnât prevent external users from using e.g. pyOpenSSL
or another external binding to perform things that canât be exposed across disparate underlying implementations.
I tried to answer that here, but I can elaborate some more :
- Windowsâ TLS stack (Schannel) does not change in incompatible ways very frequently: it dates back to 2003 and has supported TLS 1.2 since 2008 and TLS 1.3 since 2021, meaning that the bulk of Windows CPython users (especially on newer CPython releases) should have a fully TLS 1.3 stack.
- macOS has an unfortunately spotty TLS story: Secure Transport is the old (now âlegacyâ) API, while Network.framework is considered the âmodernâ API. This poses challenges for CPython and this proposal, since Network.framework does not enable the same kind of memory channel design (it wants to completely own the state machine and TLS lifecycle).
My TL;DR of the situation is âWindows should be straightforward, macOS will be a PITA unless we use a legacy API.â But IMO, per my original comment, there is still significant value in a new TLS API that still uses OpenSSL under the hood on macOS, since an independent motivation here is reducing unnecessary OpenSSL abstraction leaks even if the underlying implementation is still OpenSSL.
I think thereâs a lot of independent value in doing this! Integrating into the platform trust store is arguably more important than using the platform TLS stack; IMO itâd be a âwinâ if CPython could use the platform trust store and was able to pare down the OpenSSL specifics in the current ssl
API without actually having to remove the OpenSSL dep immediately. I believe one current blocker with truststore
is that it canât create SSLContext
s for the purposes of client auth, but @sethmlarson may have some thoughts about that
As an entirely separate comment, on the topic of an âinsecureâ flag: @jvdprng pointed out that a less footgun-y version of an âinsecureâ mode would be to instruct users to construct a trust store containing the peerâs certificate, meaning that the connection would be âinsecureâ in the ânot chained through a root of trustâ sense but âsecureâ in the ânot blindly accepting any TOFUâd connectionâ sense.
Iâm curious whether people have any thoughts on that approach â IMO it would make the overall APIs here simpler and would make the most common âinsecureâ patterns more secure.
Also, to give an update: weâre finalizing the memory channel version today, and weâll make the repository containing our draft interfaces + an example wrapper over the current ssl
module public by EOD tomorrow!
This is not so much about numbers, but rather standards SMTP port 25 is used for communication between email servers (MTAs), while the other ports are used by mail clients (MUAs) to talk to their email servers. See e.g. Simple Mail Transfer Protocol - Wikipedia (the âPortsâ section).
Now, whether mail servers use TLS on the port 25 connection via STARTTLS or not depends on their configuration. Many mail servers use opportunistic TLS, i.e. they try to switch to TLS on the connection as soon as possible, but will downgrade to plain text in case the other side doesnât accept TLS (or doesnât provide compatible cipher suites).
Fortunately, today, most mail server do accept TLS connections and you can check this by looking at the headers in your emails. If the Received:
headers mention ESMTPS the connection will have successfully used STARTTLS to encrypt traffic.
Now, as for numbers I did look around a bit, but could only find this website by Gmail: Google Transparency Report which indicates that 98-99% of the traffic they receive or send uses TLS (that is STARTTLS succeeded).
Sounds like a plan⊠except that Iâd add a
-
- A new
tlslib
module is developed and put on PyPI to gather experience and feedback.
- A new
Unfortunately, the primary advantage of using the OS stacks (all servers trusted by the client are trusted by Python) means that the situation where your client doesnât trust a server belongs entirely to the end user. Any app wanting to provide an âinsecureâ option would have to provide a âconfigure your trust store manuallyâ option, when the real thing to do is for the user to configure their system to trust the otherwise untrusted server.
The need to ignore verification entirely is an escape hatch. Nobody should really be building it into their app, but we know from experience that there are enough scenarios that need an escape hatch in order to function that we ought to provide it.
Iâd expect that at least Network.framework and possibly also SecureTransport suffer from being not safe to use in programs using fork without exec, which is something we ran into with several higher-level APIs. Thatâs why multiprocessing uses the âspawnâ start method on macOS.
Also, Network.framework not only wants to own the state machine, AFAIK it is an async framework itself (layered on top of libdispatch) which doesnât help.
I wouldnât use SecureTransport because it is deprecated and might get removed at some point.
So yeah, system TLS libraries on macOS is a PITA ;-).
The OpenSSL dependency is used for more than just ssl
, it is also used in hashlib by default. There are fallback implementations for these hash functions, but AFAIK those are less performant.
TBH I donât fully understand why removing a dependency on OpenSSL would be a good thing overall. On the plus side there wouldnât have to be emergency security releases when thereâs a security patch for OpenSSL, but on the minus side binding to multiple system TLS stacks would increase the load on core devs.
I wonder whether this is uncommon enough to justify needing the additional dependency of an actual OpenSSL module?
I fully expect that one of the primary scenarios on servers of the new module will be to install the old module, simply because the subset of functionality needed by the vast majority of clients is relatively narrow, compared to the broad range of functionality needed by servers.
That said, it may also fit nicely enough into the new model to create the new socket-like object with âTLS if you can, unencrypted if notâ (which ought to be easy enough if itâs internally linked to a memory channel), and then allow upgrading the unencrypted connection later (by adding the memory channel and starting to pass messages through it).[1] So itâs possible that this is easily handled, provided the recipient of the first TLS packet knows that itâs coming.
If I have the wrong idea here itâs entirely my own ignorance, so feel free to correct me. â©ïž
At least on Windows, the benefit is perf (without even trying Iâve been able to 2x-20x throughput with native APIs - higher level APIs than weâre talking here, unfortunately) and configuration (OpenSSL simply canât implement both certificate parsing/verification and integrate with the OS doing the same).
With OpenSSL, users often have to convert and distribute certificates, and configure random (to them) environment variables just in order to access their own sites that their IT department has already deployed certs for. These donât auto-rotate, while IT probably auto-rotates them, which means periodic downtime. And in some systems the public half of the certificate is also considered a secret - native verification can handle this, but OpenSSL cannot/will not, which means compromising your security configuration, which often means rewriting code in another language that doesnât depend on OpenSSLâŠ
Is this specific benefit not covered by truststore? Iâm not qualified to comment on the other aspects of this proposal, but from my experience the most significant problem with Python networking in a corporate environment was that the IT department had everything set up so that browsers and all the standard tools worked, but Python programs didnât[1]. And worse still, because IT handled everything else, getting the information I needed to configure Python to work was difficult, if not impossible. Anything that fixes that issue, whether itâs this proposal or truststore, would be a major improvement IMO.
This actually goes beyond certificates, and includes things like proxies as well â©ïž
Certificates are, provided youâre in an environment where ctypes is allowed (not something I get to assume, these days). Iâve got a private non-ctypes module that does the same thing, which some of my teams use, but both do the not-quite-right thing of entirely replacing OpenSSLâs certificate handling with the Windows API that does the same thing, but without necessarily applying other policy around that.
Unfortunately itâs such a deep rabbit hole of things to do to Get It Rightâą that I just canât imagine managing it with a hybrid TLS stack (e.g. should be we OCSP stapling? For this host? Is leaf certificate trust sufficient today or do we need root trust? I donât even know where these policy settings live, but theyâre all options/addons to the native API, so I have to assume they might be set).
Completely aside from any security implications, relying on system-provided libraries rather than OpenSSL would also be a major benefit to the iOS build. OpenSSL support on iOS exists, but isnât great; but more importantly, OpenSSL is a major contributor to the size of iOS builds. This is especially problematic on iOS because every app needs its own copy of Python (as there are no shared resources other than the ones provided by the system).
I will admit Iâm not especially familiar with the system provided crypto libraries on iOS; but AIUI, they are essentially the same as those provided by macOS, so any solution that works for macOS should (cough) also work for iOS. This sample project was provided at the time the macOS OpenSSL âdeprecationâ occurred as an example for how to migrate OpenSSL usage patterns to macOS, and the same code is iOS8+ compatible.
There are a few more protocols using STARTTLS-like upgrades to TLS, see e.g. Opportunistic TLS - Wikipedia, but Iâm sure there are more, since the net has slowly been moving towards TLS and there are plenty older protocols which were defined at a time when TLS was not a common thing to have.
XMPP[1] and LDAP[2] are certainly protocols which are in wide usage today and they need client side TLS upgrade support to be secure.
Itâs also possible that you may have to send emails to a LAN mail relay using port 25 and STARTTLS (e.g. if the relay or firewall doesnât support port 587 submission).
Using e.g. pyOpenSSL as a fallback solution would remain a possibility (provided this gets continued maintenance), but if possible, I think the new TLS module should try to provide functionality to support such dynamic TLS connection upgrades as well.
For reference, see RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core â©ïž
LDAP also has a non-standard TLS variant called LDAPS, but this was deprecated in favor of the STARTTLS functionality on the standard LDAP port. See Lightweight Directory Access Protocol - Wikipedia (âProtocol sectionâ). â©ïž
My point about âuncommonâ is really that our 99%[citation needed] case is HTTPS, and anyone who needs other protocols is going to be aware enough of it to take an additional step on Windows/macOS/iOS to add an additional library (I expect OpenSSL and probably the whole ssl module to remain on platforms where itâs the best way to provide support, and just become optional-but-installable on others).
I know going from zero 3rd-party packages to one is the hardest step, but are we really looking at people maintaining servers on top of these protocols with zero 3rd party packages these days?
I think the new TLS module should try to provide functionality to support such dynamic TLS connection upgrades as well.
I agree, but I donât think it should be required for the new module to be considered a success[1] (which you also seem to imply with âtryâ, but letâs also not over-focus on this aspect or people will get the impression that itâs the most important part )
Unless of course, we ship without it and everyone hates it because itâs not there. â©ïž
I know going from zero 3rd-party packages to one is the hardest step, but are we really looking at people maintaining servers on top of these protocols with zero 3rd party packages these days?
For servers, I agree, but for client protocols in common use (such as SMTP, LDAP, XMPP and probably a few others as well), I think we should continue to provide support for upgrading existing socket connections to TLS.
Iâm mostly trying to make the point that outright rejecting the idea of wrap existing sockets for use with TLS is not going in the right direction. The same goes for rejecting the idea to have the user decide whether s/he wants certificate verification or not. Both fall into the âpracticality beats purityâ category.
To your point, though: I agree that we have hashed this out well enough in this discussion. So letâs see what can be done in a prospective tlslib
implementation to address those two aspects and how well the lib can hide OS level differences in how TLS is handled. If OpenSSL is kept as additional cross-platform âpluginâ (possibly via the pyOpenSSL module or cryptography), I think weâd be well set up for the future.
OpenSSL is a major contributor to the size of iOS builds. This is especially problematic on iOS because every app needs its own copy of Python (as there are no shared resources other than the ones provided by the system).
Android uses BoringSSL internally, but that isnât part of the public API, and on newer Android versions theyâve actually modified the dynamic linker to prevent you from using it directly.
So the only system-provided SSL API on Android is the Java standard library. I suppose this PEP could be implemented on top of that using JNI, but it probably wouldnât be worth the effort. OpenSSL is only 4 MB, which isnât a major problem in the context of a modern phone.
One thing that I think Iâve implied a number of times but havenât said outright is that Iâd also like to see this be usable totally independently of the standard socket
module, in part because socket
on Windows is a stack of about three compatibility layers, but also because I think the more that goes behind the new API the better in terms of portability and optimisation.
So Iâm picturing three major sections of an API (and possibly the second can be implementation-independent):
- a memory channel implementation of TLS (local user sends/receives messages to the channel and then either uses the data or posts the message back to the remote)
- âwrapped socketâ abstraction that handles this send/receive for an existing socket-like object
- higher-level APIs for common protocols
The third is already available in terms of standard sockets, of course, and Iâd expect those to continue to work. But by having a leak-free abstraction we can provide more efficient implementations. (As an analogy, shutil.copy2
can be implemented with âstandardâ primitives, such as open()
, but thatâs not specified as part of the API, so our implementation can use a native copy function when available. On Windows at least, this is many times faster, more robust, and behaves more like an OS copy.)
It might be that only HTTPS is valuable enough to provide on its own these days, as most platforms seem to have a dedicated HTTP API (possibly web sockets or FTP as well?). I would really like to be able to take advantage of the dedicated platform APIs for platforms that have them.[1]
Without having to maintain a single library that works efficiently on all platforms. Nobody wants to do that, so a pluggable backend model with âuniversalâ fallback to Python+OpenSSL seems a great way to foster development for those platforms that can benefit from it. â©ïž
I think thereâs a lot of independent value in doing this! Integrating into the platform trust store is arguably more important than using the platform TLS stack; IMO itâd be a âwinâ if CPython could use the platform trust store and was able to pare down the OpenSSL specifics in the current
ssl
API without actually having to remove the OpenSSL dep immediately. I believe one current blocker withtruststore
is that it canât createSSLContext
s for the purposes of client auth, but @sethmlarson may have some thoughts about that
I also think this would be a big win, it has the same problem with Secure Transport in that if the APIs are removed we have no way forward. For most people this would be the biggest improvement (no more patching certifi!) and if weâre not going to be able to get away from OpenSSL entirely having truststore-like support in the ssl
module makes sense to me.
I havenât looked at any server implementations using SChannel or Secure Transport, we didnât implement client auth in truststore because we only needed server auth for truststore to be useful for pip
Certificates are, provided youâre in an environment where ctypes is allowed (not something I get to assume, these days). Iâve got a private non-ctypes module that does the same thing, which some of my teams use, but both do the not-quite-right thing of entirely replacing OpenSSLâs certificate handling with the Windows API that does the same thing, but without necessarily applying other policy around that.
The ctypes bit is easily fixed if weâd include the functionality in the standard library. Itâs not that hard to rewrite those bits in C ;-).
The hard bit is to verify that the new functionality does indeed follow the expected platform semantics for certificate validation and then implement the missing bits âsomehowâ. From what I recall from previous discussions the trust store policy bits for Windows and macOS are different from what vanilla OpenSSL does (as you already mention).