[Python-Dev] PEP 476: Enabling certificate validation by default!

Nick Coghlan ncoghlan at gmail.com
Mon Sep 1 08:05:18 CEST 2014

On 1 September 2014 11:10, R. David Murray <rdmurray at bitdance.com> wrote:
> It sounds like this would address my concerns as well (I don't really
> care *how* it is implemented as long as I don't have to touch the
> code of a third party application when I upgrade my python version to
> 3.5...remember, the context here is backward compatibility concerns).
> Does it address the issue of accepting an invalid cert, though?

That's actually an interesting question, as the PEP doesn't currently
propose adding any new global configuration knobs to the ssl or
httplib modules - it just proposes switching httplib from the legacy
(but fully backwards compatible) ssl._create_stdlib_context() API to
the newer (but potentially backwards incompatible in some
environments) ssl.create_default_context() API.

Having the ssl module import an sslcustomize module at the end
wouldn't be enough unless the appropriate APIs were put in place to
allow things to be configured at a process global level.

One possible way to do that would be to provide a central context
factory mapping that provide a module specific SSL context creator.
We'd seed it appropriately for the stdlib modules where we wanted to
use the legacy context definition, but it would default to using

Under that kind of model, the first change we would actually make is
to make ssl._create_stdlib_context() public under a suitable name,
let's say ssl.create_legacy_context()

Independenting of any other changes, exposing
ssl.create_legacy_context() like that would also make it
straightforward for folks to opt in to the old behaviour as an interim
hack in a way that is easy to grep for and fix later (it's also
something a linter can easily disallow).

The second change would be to provide a mapping from arbitrary names
to context factories in the ssl module that defaults to

    named_contexts = defaultdict((lambda name: create_default_context))

(A more accurate name would be "named_context_factory", but I think
"named_contexts" reads better. Folks will learn quickly enough that it
actually stores context factories rather than prebuilt context

The third change would be to replace all calls to
"ssl._create_stdlib_context()" with calls to
"ssl.named_contexts[__name__]()" instead.

The final change would be to seed the context factory map
appropriately for the standard library modules where we wanted to keep
the *old* default:

    for modname in ("nntplib", "poplib", "imaplib", "ftplib",
"smtplib", "asyncio.selector_events", "urllib.request",
        named_contexts[modname] = create_legacy_context

The list I have above is for *all* current uses of
"sss._create_stdlib_context". The backwards incompatible part of PEP
476 would then just be about removing names from that list (currently
just "http.client", but I'd suggest "asyncio.selector_events" as
another candidate, taking advantage of asyncio's provisional API

The "revert to 3.4 behaviour" content for sslcustomize.py would then just be:

    import ssl
    ssl.named_contexts["http.client"] = ssl.create_legacy_context

However, someone that wanted to also enforce SSL properly for other
standard library modules could go the other way:

    import ssl
    for modname in ("nntplib", "poplib", "imaplib", "ftplib",
"smtplib", "urllib.request"):
       ssl.named_contexts[modname] = ssl.create_default_context


Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia

More information about the Python-Dev mailing list