[Python-Dev] Simplify and unify SSL verification

Christian Heimes christian at python.org
Thu Nov 7 22:42:30 CET 2013


Am 07.11.2013 21:45, schrieb Antoine Pitrou:
> I'm in favour but I think 3.5 is too early. Keep in mind SSLContext is
> quite young.

It's available since 3.3

>> - deprecate implicit verify_mode=CERT_NONE. Python 3.5 will default
>>    to CERT_REQUIRED.
> 
> -0.9. This breaks compatibility and doesn't achieve anything, since
> there's no reliable story for CA certs.

I'd like to move to "secure by default". The CA cert situation is solved
on most platforms.

> Service matching is an application protocol level thing, it isn't
> strictly part of SSL. I'd feel warmer if you removed the "by default" part.

Hostname matching is done through an explicit call to
SSLSocket.check_cert(). All Python stdlib libraries like smtplib, ftplib
etc. are going to verify the hostname by default. 3rd party libraries
have to call check_cert() explicitly.

>> - add ssl.get_default_context() to acquire a default SSLContext object
>>    if the context argument is None. (Python 3.4)
> 
> I think I'm against it, for two reasons:
> 
> 1. It will in the long term create compatibility and/or security issues
> (we'll have to keep fragile legacy settings if we want to avoid breaking
> existing uses of the default context)
> 
> 2. SSLContexts are mutable, which means some code can affect other
> code's behaviour without even knowing. It's a recipe for subtle bugs and
> security issues.
> 
> Applications and/or higher-level libraries should be smart enough to
> choose their own security preferences, and it's the better place to do
> so anyway.

You misunderstood me. I'm not proposing a global SSLContext object but a
factory function that creates a context for Python stdlib modules. Right
now every urllib, http.client, nntplib, asyncio, ftplib, poplib and
imaplib have duplicated code. I'd like to have ONE function that creates
and configures a SSLContext object with sensible default values for
Python stdlib.

3rd party apps are free to create their own SSLContext object.


> The check_cert() function wouldn't achieve anything besides calling
> match_hostname(), right?

It does more: http://pastebin.com/gKi7N2WM

> I'm against calling it check_cert(), it's too generic and only induces
> confusion.

Do you have an idea for a better name?

> 
>> The SSLContext's check_cert option will support four values:
>>
>> None (default)
>>    use match_hostname() if verify_mode is CERT_OPTIONAL or
>>    CERT_REQUIRED
>> True
>>    always use match_hostname()
>> False
>>    never use match_hostname()
> 
> What is the hostname that the cert is matched against?

The library code must provide as argument to check_cert or it's taken
from server_hostname if no hostname is provided.

> And why is there an "initiator" object? Personally I prefer to avoid
> passing opaque user data, since the caller can use a closure anyway.
> And what are the **kwargs?

No, they can't use a closure. The callback function is part of the
SSLContext object. The context object can be used by multiple SSLSocket
objects. The **kwargs make it possible to pass data from the caller of
check_cert() to the callback function of the SSLContext instance.

> So what does this bring compared to the statu quo? I thought at least
> sock.check_cert() would be called automatically, but if you have to call
> it yourself it starts looking pointless, IMO.

As you have said earlier, cert matching is not strictly a SSL connection
thing. With the check_cert feature you can do stuff like validation of
self-signed certs with known hash sums:

def check_cert_cb(sslsock, hostname, initiator, **kwargs):
    # real code would use SPKI
    certdigest = sha1(sslsock.getpeercert(True)).digest()
    if hostname == "my.host.name" and certdigest == b"abcdef...":
        return True
    do_other_check(sslsock, hostname)


ctx = SSLContext(PROTOCOL_TLSv1, check_cert_cb)
ctx.verify_mode = CERT_NONE

httpscon = http.client.HTTPSConnection("my.host.name", 443, context=ctx)
httpscon = http.client.HTTPSConnection("my.host.name", 443, context=ctx)
httpscon = http.client.HTTPSConnection("my.host.name", 443, context=ctx)

Christian



More information about the Python-Dev mailing list