[go: up one dir, main page]

|
|
Log in / Subscribe / Register

A unified TLS API for Python

By Jake Edge
January 19, 2017

Back at the 2016 Python Language Summit, Cory Benfield and Christian Heimes gave a presentation on the future of the language's ssl module. It provides the standard library's TLS support, but suffers from a number of problems, much of it baggage that has accreted over the years. Now Benfield has proposed a PEP to, essentially, move on from ssl, and to create a set of abstract base classes (ABCs) that would define the API for TLS going forward.

Benfield posted the PEP (as yet without a number) to the Python Security SIG mailing list, but it has been in progress since last October in a GitHub repository. Since ssl is used by the pip installation tool, it is important that it works for all of the systems that Python gets installed on, Benfield said. But it is reliant on OpenSSL, which is not well supported on macOS and Windows:

This problem ultimately stems from the fact that Python has never exposed the ability to use TLS backends other than OpenSSL in the standard library. As Christian and I discussed back at PyCon US 2015, the ssl module would more properly be called the openssl module, due to exposing many OpenSSL-specific concepts and behaviours in its API. This has meant that the Python ecosystem is overwhelmingly also an OpenSSL ecosystem, which is problematic on Windows and macOS (and Unices that for whatever reason aren't interested in shipping an OpenSSL), as it has meant that Python needs to bring its own OpenSSL, and that it is troublesome to interact with the system trust database.

To address that, a more generic API for TLS is needed, he said. There are two implementations that he considered:

The first is to have this module be a generic concrete implementation that can be compiled against multiple TLS backends (like curl). This would require that all the relevant bindings be built into the standard library from the get-go, which provides a substantial maintenance burden on a team of people that are already understaffed maintaining the ssl module alone. The second approach is to define a generic high-level TLS interface that provides a minimal usable interface that can be implemented by both first- and third-party modules. This would allow the standard library to continue to ship with only exactly what it needs (for example, OpenSSL, SecureTransport and SChannel), but would give those who want to use more esoteric TLS choices (NSS, GnuTLS, mbedTLS are some examples) an API that they can implement that will guarantee that complying modules can use the appropriate TLS backend.

As might be guessed based on that description, Benfield has chosen the latter approach. The in-progress PEP proposes a set of ABCs that are meant to allow any underlying TLS library provide a compliant implementation. That would allow users, distributors, and others to swap in other TLS libraries as needed—something that is not really possible with today's ssl.

The PEP outlines the various pieces that need attention and where they are currently implemented in ssl (if they are):

  1. Configuring TLS, currently implemented by the SSLContext class in the ssl module.
  2. Wrapping a socket object, currently implemented by the SSLSocket class in the ssl module.
  3. Providing an in-memory buffer for doing in-memory encryption or decryption with no actual I/O (necessary for asynchronous I/O models), currently implemented by the SSLObject class in the ssl module.
  4. Specifying TLS cipher suites. There is currently no code for doing this in the standard library: instead, the standard library uses OpenSSL cipher suite strings.
  5. Specifying application-layer protocols that can be negotiated during the TLS handshake.
  6. Specifying TLS versions.
  7. Reporting errors to the caller, currently implemented by the SSLError class in the ssl module.
  8. Specifying certificates to load, either as client or server certificates.
  9. Specifying which trust database should be used to validate certificates presented by a remote peer.

The PEP then proposes ABCs to support each of those (though the TLS cipher suite specification is still listed as "Todo"). It also briefly looks at the other standard library modules that will need to be revised to use the interfaces. Benfield lists seven different modules (such as asyncio, http.client, imaplib, and smtplib) that would need to change.

The reaction to the posting was largely favorable, though there were plenty of technical concerns mentioned, many of which have been addressed with changes to the PEP. It is already a large specification, but there were questions about additional features. Benfield, Heimes, and others were resistant to calls for more features in the first round, though Heimes brought up the need to support SRV-ID (used to identify different service types) eventually. Benfield wanted to take a wait-and-see attitude for features like that:

The advantage of this API is that it would be extensible. We can extend it as needed over time, we don't need to shove everything in at once. So I'm inclined to want to defer this until we see what the implementations actually do.

Heimes agreed and, in another sub-thread, pushed back on some somewhat exotic features that were being proposed. Though Benfield sees the server-side TLS support as mandatory, Heimes would even be willing to leave that behind in order to get something out there more quickly.

There is, it seems, a deadline of sorts at hand. As Donald Stufft reported to the Distutils SIG mailing list recently, the content delivery network (CDN) that PyPI and other Python infrastructure uses will be phasing out TLS 1.0 and 1.1 support over the next year and a half. Some solution needs to be in place before June 2018 or macOS pip clients will not be able to run; others may well be affected too.

So Heimes is interested in a bare-bones solution, but one that gets the job done: "Personally I would rather remove half of the PEP than add new things." He is concerned that more features will make it that much harder for implementers in the time frame available. Stufft agreed as well: "Getting too lost in the weeds over advanced features like hot-config-reload I agree is a bad use of resources."

But Wes Turner was concerned that adding a TLS configuration object later, as was being advocated, would be hard to do. Nick Coghlan, on the other hand, thought that was the proper approach:

The appropriate time to define that lowest-common-denominator configuration format is *after* there is a working programmatic API that covers at least the 3 major implementations of interest (OpenSSL, SecureTransport, SChannel), and hopefully a few other implementations as well (e.g. NSS, BoringSSL).

It turns out that Turner was actually looking beyond simply configuring the underlying library and was instead concerned with adding an interface to determine whether a given configuration was consistent with a particular security policy. It is something that will need to be addressed, eventually, but once again is "vastly beyond the scope of the problem I'm trying to solve here", Benfield said. It is tempting to add all of the needed pieces at once, he continued, so that all of the TLS dragons can be slayed in one go, but he has a different vision moving forward:

I want us to develop a culture around TLS functionality where we are willing to continually iterate on it, and where the community feels willing-and-able to provide extensions and new functionality incrementally, rather than all at once. Let's not have the perfect be the enemy of the good here. I highly recommend that people who are interested in TLS policy workshop a separate PEP that discusses what we should build to resolve that issue. But we should not block solving problem A (how do we use something that isn't OpenSSL with the standard library) on solving problem B (how do we define and enforce TLS policy).

A more iterative culture around TLS is likely something that Python needs. The ssl module has been a problem child for some time now, at least partly because it mostly works—though some of its defaults are insecure—and lacks for developer time. But protocols change over time and older versions get deprecated. At the moment, that is providing some impetus to speed up the changes that have been needed for some time, which will help. But it would be nice to see proactive efforts at keeping up with TLS down the road.


Index entries for this article
SecurityPython
SecurityTransport Layer Security (TLS)


to post comments

Revocation

Posted Jan 19, 2017 2:30 UTC (Thu) by biergaizi (guest, #92498) [Link] (7 responses)

The ssl module in early Python doesn't have certain features that are essential for a secure connection, things has became much better in Python 3.5, but anyway you can manually replace the insecure protocol versions and ciphersuits, enable or perform hostname checkes, etc. I have written a function that returns TLS socket as secure as the current Python runtime can provide.

Nevertheless, currently there is almost no way to check if the certificate has been revoked on the fly while making a TLS connection. The only choice is to load a prepared CRL file before you make a connection, which is unpractical for most application, ant not supported in all versions. OCSP is not implemented at all besides some undependable simple implementation... This issue must be addressed.

Not only Python, I believe there are similar problems of SSL/TLS libraries in Ruby, Perl, Go. Hopefully Python will be a good start.

Revocation

Posted Jan 19, 2017 6:38 UTC (Thu) by noxxi (subscriber, #4994) [Link] (3 responses)

> ...I believe there are similar problems of SSL/TLS libraries in Ruby, Perl, Go.

SSL is not in Perl CORE but the standard modules are Net::SSLeay for low level and IO::Socket::SSL for higher level (i.e. easy) access. IO::Socket::SSL has OCSP support since 05/2014: if the server supports OCSP stapling revocation checks are enabled by default and otherwise it is enough to call $ssl->resolve_blocking() to do the necessary OCSP lookups.

Last time I looked at Ruby they has some OCSP libraries which essentially just exposed the ugly and (at least at this time) undocumented OCSP API from OpenSSL and thus were not really useful in practice.

Revocation

Posted Jan 19, 2017 10:36 UTC (Thu) by jwilk (subscriber, #63328) [Link] (2 responses)

How can I do non-stapled OCSP checks with LWP?

Revocation

Posted Jan 19, 2017 20:21 UTC (Thu) by noxxi (subscriber, #4994) [Link] (1 responses)

LWP does not use the OCSP functionality of IO::Socket::SSL. It also does not expose the SSL socket so one could do it by hand. It can probably be hacked somehow from outside in the code (like most can be done in Perl:) but maybe one should file a feature request to get this functionality.

Revocation

Posted Jan 20, 2017 11:52 UTC (Fri) by t-8ch (subscriber, #90907) [Link]

I am quite sure LWP on Debian jessie uses at least OCSP stapling implicitly.
My monitoring broke during a recent outage of the Lets Encrypt OCSP outage.
(The error messages are not really helpful/specific, just "Could not connect")

Revocation

Posted Jan 19, 2017 9:12 UTC (Thu) by shane (subscriber, #3335) [Link]

> I have written a function that returns TLS socket as secure as the current Python runtime can provide.

Can you provide a link to this? I've just started working on some code using TLS and would love to see what you use as a secure version!

Revocation

Posted Jan 19, 2017 9:20 UTC (Thu) by tiran (guest, #94212) [Link]

I added CRL verification and AIA information to Python a while, because it was a low hanging fruit. It's not recommended, though. Cory and I are working on some features to support OCSP verification. It's going to be OCSP stapling only, though. We both agree that OCSP requests are a bad idea.

Revocation

Posted Jan 19, 2017 11:01 UTC (Thu) by wbond (guest, #113637) [Link]

If you are interested in a TLS library for Python and supporting OSCP checks, there are two complementary libraries you may be interested in: https://github.com/wbond/oscrypto provides a TLS layer with sane defaults, and https://github.com/wbond/certvalidator does full X.509 certificate path validation (minus name constraints), including he ability to configure hard/soft fail on revocation via OCSP and CRL.

oscrypto.tls is not a drop-in replacement of the ssl module, but is designed to have a reasonable API, supports Python 2.6+, along with OpenSSL, SChannel and SecureTransport.

A unified TLS API for Python

Posted Jan 19, 2017 13:14 UTC (Thu) by richmoore (guest, #53133) [Link] (3 responses)

This needs a lot of review I suspect. I didn't see a way to specify things like the server name for SNI, name constraint checks etc. for example.

A unified TLS API for Python

Posted Jan 19, 2017 13:43 UTC (Thu) by lukasa (subscriber, #113639) [Link] (2 responses)

The server name for SNI is provided in the wrap_socket and wrap_buffer abstract methods.

You're right that name constraint checks aren't present at this time. This is for two reasons: firstly, they don't fit into the realm of "lowest common denominator". It is assumed that all concrete implementations will default to having only two verification modes: entirely on, and entirely off. If set to on, and the implementation is capable of doing name constraint checks, then it should do it. If set to off, then the implementation should not do name constraint checks, regardless of whether it is capable of them or not. Trying to actually do this if we want to define an API to do every conceivable thing that TLS implementations are capable of will take forever, and ultimately is a great example of letting the perfect be the enemy of the good. We want to take concrete steps forward, rather than design the perfect API from day one.

The second reason they aren't present at this time is because essentially no Python bindings to TLS libraries expose the functionality to control name constraint checks. For that reason, we have no APIs to look at to see what the community has decided is the "Python Way" to do this. I'd like to reduce the barrier to entry to writing bindings to TLS implementations so that we can free the community up to explore these options.

Ultimately, this API is intended not to be the complete product, but part one of an ongoing job to improve and refine the API.

(Disclosure: I am the author of the proto-PEP.)

A unified TLS API for Python

Posted Jan 19, 2017 14:39 UTC (Thu) by richmoore (guest, #53133) [Link] (1 responses)

Are the discussions of this PEP just on the python security list or is there an IRC channel etc.? I've contributed to pyopenssl and I maintain the Qt SSL support which provides an API that currently has backends for OpenSSL, SecureTransport and the WinRT SSL APIs so I've hit some of the problems you're likely to encounter already.

A unified TLS API for Python

Posted Jan 19, 2017 17:08 UTC (Thu) by lukasa (subscriber, #113639) [Link]

They are on the security-SIG list and on the GitHub that holds the PEP. Given your implementation experience, I would recommend joining the security-SIG mailing list anyway, as it sounds like you'd have relevant and sensible things to contribute!

I've spent a bunch of time today refactoring this API to try to achieve a better separation of concerns and be a bit less OpenSSL-y than it was before, so there should be a new email thread coming soon with a new draft of the PEP.


Copyright © 2017, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds