Suggested changes to verify HTTPS by default (was Re: Proposed schedule for 3.4.2)
On 9 September 2014 03:44, Alex Gaynor
*Shifts uncomfortably* it looks like presently there's not a good way to change anything about the SSL configuration for urllib.request.urlopen. It does not take a `context` argument, as the http.client API does: https://docs.python.org/3/library/urllib.request.html#module-urllib.request and instead takes the cafile, capath, cadefault args.
This would need to be updated first, once it *did* take such an argument, this would be accomplished by:
context = ssl.create_default_context() context.verify_mode = CERT_OPTIONACERT_NONE context.verify_hostname = False urllib.request.urlopen("https://something-i-apparently-dont-care-much-about", context=context)
I'd never needed to use the existing global configuration settings in urllib.request before, but it actually *does* already support setting the default opener for urllib.urlopen. To explicitly set it to use verified HTTPS by default: import ssl, urllib.request https_handler = HTTPSHandler(context=ssl.create_default_context(), check_hostname=True) urllib.request.install_opener(urllib.request.build_opener(https_handler) When the default changes, turning off verification by default for urllib.request.urlopen would look like: import ssl, urllib.request unverified_context = ssl.create_default_context() unverified_context.verify_mode = CERT_OPTIONACERT_NONE unverified_context.verify_hostname = False unverified_handler = HTTPSHandler(context=unverified_context, check_hostname=False) urllib.request.install_opener(urllib.request.build_opener(unverified_handler) However, even setting the opener like that still leaves http.client.HTTPSConnection, urllib.request.URLOpener and urllib.request.FancyURLOpener using unverified HTTPS with no easy way to change their default behaviour. That means some other approach to global configuration is going to be needed to cover the "coping with legacy corporate infrastructure" case, and I still think a monkeypatching based hack is likely to be our best bet. So, focusing on 3.4, but in a way that should be feasible to backport, the changes that I now believe would be needed are: 1. Add "context" arguments to urlopen, URLOpener and FancyURLOpener (the latter two have been deprecated since 3.3, but this would make things easier for a subsequent 2.7 backport) 2. Add a ssl._create_https_context() alias for ssl.create_default_context() 3. Change urllib.request.urlopen() and http.client.HTTPSConnection to call ssl_create_https_context() rather than ssl._create_stdlib_context() 4. Rename ssl._create_stdlib_context() to ssl._create_unverified_context() (updating call sites accordingly) To revert any given call site to the old behaviour: http.client.HTTPSConnection(context=ssl._create_unverified_context()) urllib.request.urlopen(context=ssl._create_unverified_context()) urllib.request.URLOpener(context=ssl._create_unverified_context()) urllib.request.FancyURLOpener(context=ssl._create_unverified_context()) And to revert to the old default behaviour globally: import ssl ssl._create_https_context = ssl._create_unverified_context The backport to 2.7 would then be a matter of bringing urllib, urllib2, httplib and ssl into line with their 3.4.2 counterparts. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
I'm going to leave the design up to Nick and friends for a while. Let me
know when there is a patch to review.
On Tue, Sep 9, 2014 at 3:52 AM, Nick Coghlan
On 9 September 2014 03:44, Alex Gaynor
wrote: *Shifts uncomfortably* it looks like presently there's not a good way to change anything about the SSL configuration for urllib.request.urlopen. It does not take a `context` argument, as the http.client API does:
https://docs.python.org/3/library/urllib.request.html#module-urllib.request
and instead takes the cafile, capath, cadefault args.
This would need to be updated first, once it *did* take such an argument, this would be accomplished by:
context = ssl.create_default_context() context.verify_mode = CERT_OPTIONACERT_NONE context.verify_hostname = False urllib.request.urlopen(" https://something-i-apparently-dont-care-much-about", context=context)
I'd never needed to use the existing global configuration settings in urllib.request before, but it actually *does* already support setting the default opener for urllib.urlopen.
To explicitly set it to use verified HTTPS by default:
import ssl, urllib.request https_handler = HTTPSHandler(context=ssl.create_default_context(), check_hostname=True)
urllib.request.install_opener(urllib.request.build_opener(https_handler)
When the default changes, turning off verification by default for urllib.request.urlopen would look like:
import ssl, urllib.request unverified_context = ssl.create_default_context() unverified_context.verify_mode = CERT_OPTIONACERT_NONE unverified_context.verify_hostname = False unverified_handler = HTTPSHandler(context=unverified_context, check_hostname=False)
urllib.request.install_opener(urllib.request.build_opener(unverified_handler)
However, even setting the opener like that still leaves http.client.HTTPSConnection, urllib.request.URLOpener and urllib.request.FancyURLOpener using unverified HTTPS with no easy way to change their default behaviour.
That means some other approach to global configuration is going to be needed to cover the "coping with legacy corporate infrastructure" case, and I still think a monkeypatching based hack is likely to be our best bet.
So, focusing on 3.4, but in a way that should be feasible to backport, the changes that I now believe would be needed are:
1. Add "context" arguments to urlopen, URLOpener and FancyURLOpener (the latter two have been deprecated since 3.3, but this would make things easier for a subsequent 2.7 backport) 2. Add a ssl._create_https_context() alias for ssl.create_default_context() 3. Change urllib.request.urlopen() and http.client.HTTPSConnection to call ssl_create_https_context() rather than ssl._create_stdlib_context() 4. Rename ssl._create_stdlib_context() to ssl._create_unverified_context() (updating call sites accordingly)
To revert any given call site to the old behaviour:
http.client.HTTPSConnection(context=ssl._create_unverified_context()) urllib.request.urlopen(context=ssl._create_unverified_context()) urllib.request.URLOpener(context=ssl._create_unverified_context()) urllib.request.FancyURLOpener(context=ssl._create_unverified_context())
And to revert to the old default behaviour globally:
import ssl ssl._create_https_context = ssl._create_unverified_context
The backport to 2.7 would then be a matter of bringing urllib, urllib2, httplib and ssl into line with their 3.4.2 counterparts.
Regards, Nick.
-- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
-- --Guido van Rossum (python.org/~guido)
participants (2)
-
Guido van Rossum
-
Nick Coghlan