PEP 493: simplify detection of the capability

This commit is contained in:
Nick Coghlan 2015-07-06 10:45:09 +10:00
parent f0951bcf36
commit baef915b05
1 changed files with 61 additions and 14 deletions

View File

@ -72,6 +72,25 @@ version of Python 3 (whether published directly by the Python Software
Foundation or by a redistributor). Foundation or by a redistributor).
Requirements for capability detection
=====================================
As these recommendations are intended to cover backports to earlier Python
versions, the Python version number cannot be used as a reliable means for
detecting them. Instead, the recommendations are defined to allow the presence
or absence of the feature to be determined using the following technique::
python -c "import ssl; ssl._relevant_attribute"
This will fail with `AttributeError` (and hence a non-zero return code) if the
relevant capability is not available.
The marker attributes are prefixed with an underscore to indicate the
implementation dependent nature of these capabilities - not all Python
distributions will offer them, only those that are providing a multi-stage
migration process from the legacy HTTPS handling to the new default behaviour.
Recommendation for an environment variable based security downgrade Recommendation for an environment variable based security downgrade
=================================================================== ===================================================================
@ -91,6 +110,19 @@ This approach may be used for any redistributor provided version of Python 2.7,
including those that advertise themselves as providing Python 2.7.9 or later. including those that advertise themselves as providing Python 2.7.9 or later.
Required marker attribute
-------------------------
The required marker attribute on the ``ssl`` module when implementing this
recommendation is::
_https_verify_envvar = 'PYTHONHTTPSVERIFY'
This not only makes it straightforward to detect the presence (or absence) of
the capability, it also makes it possible to programmatically determine the
relevant environment variable name.
Recommended modifications to the Python standard library Recommended modifications to the Python standard library
-------------------------------------------------------- --------------------------------------------------------
@ -112,8 +144,10 @@ Example implementation
:: ::
_https_verify_envvar = 'PYTHONHTTPSVERIFY'
def _get_https_context_factory(): def _get_https_context_factory():
config_setting = os.environ.get('PYTHONHTTPSVERIFY') config_setting = os.environ.get(_https_verify_envvar)
if config_setting == '0': if config_setting == '0':
return _create_unverified_context return _create_unverified_context
return create_default_context return create_default_context
@ -128,13 +162,10 @@ Relative to an unmodified version of CPython 2.7.9 or later, this approach
does introduce a new downgrade attack against the default security settings does introduce a new downgrade attack against the default security settings
that potentially allows a sufficiently determined attacker to revert Python that potentially allows a sufficiently determined attacker to revert Python
to the vulnerable configuration used in CPython 2.7.8 and earlier releases. to the vulnerable configuration used in CPython 2.7.8 and earlier releases.
Such an attack requires the ability to modify the execution environment of However, such an attack requires the ability to modify the execution
a Python process prior to the import of the ``ssl`` module. environment of a Python process prior to the import of the ``ssl`` module,
and any attacker with such access would already be able to modify the
Redistributors should balance this marginal increase in risk against the behaviour of the underlying OpenSSL implementation.
ability to offer a smoother migration path to their users when deciding whether
or not it is appropriate for them to implement this per-application "opt out"
model.
Recommendation for backporting to earlier Python versions Recommendation for backporting to earlier Python versions
@ -161,6 +192,19 @@ reasonable expectation that all such environments will validate HTTPS
certificates by default. certificates by default.
Required marker attribute
-------------------------
The required marker attribute on the ``ssl`` module when implementing this
recommendation is::
_cert_verification_config = '<path to configuration file>'
This not only makes it straightforward to detect the presence (or absence) of
the capability, it also makes it possible to programmatically determine the
relevant configuration file name.
Recommended modifications to the Python standard library Recommended modifications to the Python standard library
-------------------------------------------------------- --------------------------------------------------------
@ -218,9 +262,10 @@ Example implementation
:: ::
_cert_verification_config = '/etc/python/cert-verification.cfg'
def _get_https_context_factory(): def _get_https_context_factory():
# Check for a system-wide override of the default behaviour # Check for a system-wide override of the default behaviour
config_file = '/etc/python/cert-verification.cfg'
context_factories = { context_factories = {
'enable': create_default_context, 'enable': create_default_context,
'disable': _create_unverified_context, 'disable': _create_unverified_context,
@ -228,7 +273,7 @@ Example implementation
} }
import ConfigParser import ConfigParser
config = ConfigParser.RawConfigParser() config = ConfigParser.RawConfigParser()
config.read(config_file) config.read(_cert_verification_config)
try: try:
verify_mode = config.get('https', 'verify') verify_mode = config.get('https', 'verify')
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
@ -268,7 +313,7 @@ turned off system wide when using this approach is because:
(at least for the time being) (at least for the time being)
Using an administrator controlled configuration file rather than an environment Using an administrator controlled configuration file rather than an environment
variable has the essential feature of providing a smoother migraiton path, even variable has the essential feature of providing a smoother migration path, even
for applications being run with the ``-E`` switch. for applications being run with the ``-E`` switch.
@ -289,16 +334,18 @@ Example implementation
:: ::
_https_verify_envvar = 'PYTHONHTTPSVERIFY'
_cert_verification_config = '/etc/python/cert-verification.cfg'
def _get_https_context_factory(): def _get_https_context_factory():
# Check for am environmental override of the default behaviour # Check for am environmental override of the default behaviour
config_setting = os.environ.get('PYTHONHTTPSVERIFY') config_setting = os.environ.get(_https_verify_envvar)
if config_setting is not None: if config_setting is not None:
if config_setting == '0': if config_setting == '0':
return _create_unverified_context return _create_unverified_context
return create_default_context return create_default_context
# Check for a system-wide override of the default behaviour # Check for a system-wide override of the default behaviour
config_file = '/etc/python/cert-verification.cfg'
context_factories = { context_factories = {
'enable': create_default_context, 'enable': create_default_context,
'disable': _create_unverified_context, 'disable': _create_unverified_context,
@ -306,7 +353,7 @@ Example implementation
} }
import ConfigParser import ConfigParser
config = ConfigParser.RawConfigParser() config = ConfigParser.RawConfigParser()
config.read(config_file) config.read(_cert_verification_config)
try: try:
verify_mode = config.get('https', 'verify') verify_mode = config.get('https', 'verify')
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):