python-peps/pep-0493/index.html

726 lines
57 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<title>PEP 493 HTTPS verification migration tools for Python 2.7 | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0493/">
<link rel="stylesheet" href="../_static/style.css" type="text/css">
<link rel="stylesheet" href="../_static/mq.css" type="text/css">
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light">
<link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark">
<link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss">
<meta property="og:title" content='PEP 493 HTTPS verification migration tools for Python 2.7 | peps.python.org'>
<meta property="og:description" content="PEP 476 updated Pythons default handling of HTTPS certificates in client modules to align with certificate handling in web browsers, by validating that the certificates received belonged to the server the client was attempting to contact. The Python 2....">
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0493/">
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
<meta property="og:image:alt" content="Python PEPs">
<meta property="og:image:width" content="200">
<meta property="og:image:height" content="200">
<meta name="description" content="PEP 476 updated Pythons default handling of HTTPS certificates in client modules to align with certificate handling in web browsers, by validating that the certificates received belonged to the server the client was attempting to contact. The Python 2....">
<meta name="theme-color" content="#3776ab">
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
<title>Following system colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"></circle>
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
<title>Selected dark colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
<title>Selected light colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
</svg>
<script>
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
</script>
<section id="pep-page-section">
<header>
<h1>Python Enhancement Proposals</h1>
<ul class="breadcrumbs">
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li>
<li><a href="../pep-0000/">PEP Index</a> &raquo; </li>
<li>PEP 493</li>
</ul>
<button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())">
<svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg>
<span class="visually-hidden">Toggle light / dark / auto colour theme</span>
</button>
</header>
<article>
<section id="pep-content">
<h1 class="page-title">PEP 493 HTTPS verification migration tools for Python 2.7</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Alyssa Coghlan &lt;ncoghlan&#32;&#97;t&#32;gmail.com&gt;,
Robert Kuska &lt;rkuska&#32;&#97;t&#32;redhat.com&gt;,
Marc-André Lemburg &lt;mal&#32;&#97;t&#32;lemburg.com&gt;</dd>
<dt class="field-even">BDFL-Delegate<span class="colon">:</span></dt>
<dd class="field-even">Barry Warsaw</dd>
<dt class="field-odd">Status<span class="colon">:</span></dt>
<dd class="field-odd"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd>
<dt class="field-even">Type<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
<dt class="field-odd">Created<span class="colon">:</span></dt>
<dd class="field-odd">10-May-2015</dd>
<dt class="field-even">Python-Version<span class="colon">:</span></dt>
<dd class="field-even">2.7.12</dd>
<dt class="field-odd">Post-History<span class="colon">:</span></dt>
<dd class="field-odd">06-Jul-2015, 11-Nov-2015, 24-Nov-2015, 24-Feb-2016</dd>
<dt class="field-even">Resolution<span class="colon">:</span></dt>
<dd class="field-even"><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2016-March/143450.html">Python-Dev message</a></dd>
</dl>
<hr class="docutils" />
<section id="contents">
<details><summary>Table of Contents</summary><ul class="simple">
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a><ul>
<li><a class="reference internal" href="#alternatives">Alternatives</a></li>
</ul>
</li>
<li><a class="reference internal" href="#scope-limitations">Scope Limitations</a></li>
<li><a class="reference internal" href="#requirements-for-capability-detection">Requirements for capability detection</a></li>
<li><a class="reference internal" href="#feature-configuration-api">Feature: Configuration API</a><ul>
<li><a class="reference internal" href="#feature-detection">Feature detection</a></li>
<li><a class="reference internal" href="#specification">Specification</a></li>
<li><a class="reference internal" href="#security-considerations">Security Considerations</a></li>
</ul>
</li>
<li><a class="reference internal" href="#feature-environment-based-configuration">Feature: environment based configuration</a><ul>
<li><a class="reference internal" href="#id1">Feature detection</a></li>
<li><a class="reference internal" href="#id2">Specification</a></li>
<li><a class="reference internal" href="#example-implementation">Example implementation</a></li>
<li><a class="reference internal" href="#id3">Security Considerations</a></li>
<li><a class="reference internal" href="#interaction-with-python-virtual-environments">Interaction with Python virtual environments</a></li>
</ul>
</li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#backporting-this-pep-to-earlier-python-versions">Backporting this PEP to earlier Python versions</a><ul>
<li><a class="reference internal" href="#id4">Feature detection</a></li>
<li><a class="reference internal" href="#id5">Specification</a></li>
<li><a class="reference internal" href="#id6">Example implementation</a></li>
<li><a class="reference internal" href="#id7">Security Considerations</a></li>
<li><a class="reference internal" href="#id8">Interaction with Python virtual environments</a></li>
</ul>
</li>
<li><a class="reference internal" href="#backporting-pep-476-to-earlier-python-versions">Backporting PEP 476 to earlier Python versions</a><ul>
<li><a class="reference internal" href="#id9">Feature detection</a></li>
<li><a class="reference internal" href="#recommended-modifications-to-the-python-standard-library">Recommended modifications to the Python standard library</a></li>
<li><a class="reference internal" href="#recommended-file-location">Recommended file location</a></li>
<li><a class="reference internal" href="#recommended-file-format">Recommended file format</a></li>
<li><a class="reference internal" href="#id10">Example implementation</a></li>
<li><a class="reference internal" href="#id11">Security Considerations</a></li>
<li><a class="reference internal" href="#id12">Interaction with Python virtual environments</a></li>
<li><a class="reference internal" href="#origins-of-this-recommendation">Origins of this recommendation</a></li>
</ul>
</li>
<li><a class="reference internal" href="#recommendation-for-combined-feature-backports">Recommendation for combined feature backports</a><ul>
<li><a class="reference internal" href="#id13">Example implementation</a></li>
</ul>
</li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
</details></section>
<section id="abstract">
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
<p><a class="pep reference internal" href="../pep-0476/" title="PEP 476 Enabling certificate verification by default for stdlib http clients">PEP 476</a> updated Pythons default handling of HTTPS certificates in client
modules to align with certificate handling in web browsers, by validating
that the certificates received belonged to the server the client was attempting
to contact. The Python 2.7 long term maintenance series was judged to be in
scope for this change, with the new behaviour introduced in the Python 2.7.9
maintenance release.</p>
<p>This has created a non-trivial barrier to adoption for affected Python 2.7
maintenance releases, so this PEP proposes additional Python 2.7 specific
features that allow system administrators and other users to more easily
decouple the decision to verify server certificates in HTTPS client modules
from the decision to update to newer Python 2.7 maintenance releases.</p>
</section>
<section id="rationale">
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
<p><a class="pep reference internal" href="../pep-0476/" title="PEP 476 Enabling certificate verification by default for stdlib http clients">PEP 476</a> changed Pythons default behaviour to align with expectations
established by web browsers in regards to the semantics of HTTPS URLs:
starting with Python 2.7.9 and 3.4.3, HTTPS clients in the standard library
validate server certificates by default.</p>
<p>However, it is also the case that this change <em>does</em> cause problems for
infrastructure administrators operating private intranets that rely on
self-signed certificates, or otherwise encounter problems with the new default
certificate verification settings.</p>
<p>To manage these kinds of situations, web browsers provide users with “click
through” warnings that allow the user to add the servers certificate to the
browsers certificate store. Network client tools like <code class="docutils literal notranslate"><span class="pre">curl</span></code> and <code class="docutils literal notranslate"><span class="pre">wget</span></code>
offer options to switch off certificate checking entirely (by way of
<code class="docutils literal notranslate"><span class="pre">curl</span> <span class="pre">--insecure</span></code> and <code class="docutils literal notranslate"><span class="pre">wget</span> <span class="pre">--no-check-certificate</span></code>, respectively).</p>
<p>At a different layer of the technology stack, Linux security modules like
<code class="docutils literal notranslate"><span class="pre">SELinux</span></code> and <code class="docutils literal notranslate"><span class="pre">AppArmor</span></code>, while enabled by default by distribution vendors,
offer relatively straightforward mechanisms for turning them off.</p>
<p>At the moment, no such convenient mechanisms exist to disable Pythons
default certificate checking for a whole process.</p>
<p><a class="pep reference internal" href="../pep-0476/" title="PEP 476 Enabling certificate verification by default for stdlib http clients">PEP 476</a> did attempt to address this question, by covering how to revert to the
old settings process wide by monkeypatching the <code class="docutils literal notranslate"><span class="pre">ssl</span></code> module to restore the
old behaviour. Unfortunately, the <code class="docutils literal notranslate"><span class="pre">sitecustomize.py</span></code> based technique proposed
to allow system administrators to disable the feature by default in their
Standard Operating Environment definition has been determined to be
insufficient in at least some cases. The specific case that led to the
initial creation of this PEP is the one where a Linux distributor aims to
provide their users with a
<a class="reference external" href="https://bugzilla.redhat.com/show_bug.cgi?id=1173041">smoother migration path</a>
than the standard one provided by consuming upstream CPython 2.7 releases
directly, but other potential challenges have also been pointed out with
updating embedded Python runtimes and other user level installations of Python.</p>
<p>Rather than allowing a plethora of mutually incompatible migration techniques
to bloom, this PEP proposes an additional feature to be added to Python 2.7.12
to make it easier to revert a process to the past behaviour of skipping
certificate validation in HTTPS client modules. It also provides additional
recommendations to redistributors backporting these features to versions of
Python prior to Python 2.7.9.</p>
<section id="alternatives">
<h3><a class="toc-backref" href="#alternatives" role="doc-backlink">Alternatives</a></h3>
<p>In the absence of clear upstream guidance and recommendations, commercial
redistributors will still make their own design decisions in the interests of
their customers. The main approaches available are:</p>
<ul class="simple">
<li>Continuing to rebase on new Python 2.7.x releases, while providing no
additional assistance beyond the mechanisms defined in <a class="pep reference internal" href="../pep-0476/" title="PEP 476 Enabling certificate verification by default for stdlib http clients">PEP 476</a> in migrating
from unchecked to checked hostnames in standard library HTTPS clients</li>
<li>Gating availability of the changes in default handling of HTTPS connections
on upgrading from Python 2 to Python 3</li>
<li>For Linux distribution vendors, gating availability of the changes in default
handling of HTTPS connections on upgrading to a new operating system version</li>
<li>Implementing one or both of the backport suggestions described in this PEP,
regardless of the formal status of the PEP</li>
</ul>
</section>
</section>
<section id="scope-limitations">
<h2><a class="toc-backref" href="#scope-limitations" role="doc-backlink">Scope Limitations</a></h2>
<p>These changes are being proposed purely as tools for helping to manage the
transition to the new default certificate handling behaviour in the context
of Python 2.7. They are not being proposed as new features for Python 3, as
it is expected that the vast majority of client applications affected by this
problem without the ability to update the application itself will be Python 2
applications.</p>
<p>It would likely be desirable for a future version of Python 3 to allow the
default certificate handling for secure protocols to be configurable on a
per-protocol basis, but that question is beyond the scope of this PEP.</p>
</section>
<section id="requirements-for-capability-detection">
<h2><a class="toc-backref" href="#requirements-for-capability-detection" role="doc-backlink">Requirements for capability detection</a></h2>
<p>As the proposals in this PEP aim to facilitate backports to earlier Python
versions, the Python version number cannot be used as a reliable means for
detecting them. Instead, they are designed to allow the presence
or absence of the feature to be determined using the following technique:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">python</span> <span class="o">-</span><span class="n">c</span> <span class="s2">&quot;import ssl; ssl.&lt;_relevant_attribute&gt;&quot;</span>
</pre></div>
</div>
<p>This will fail with <code class="docutils literal notranslate"><span class="pre">AttributeError</span></code> (and hence a non-zero return code) if
the relevant capability is not available.</p>
<p>The feature detection attributes defined by this PEP are:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">ssl._https_verify_certificates</span></code>: runtime configuration API</li>
<li><code class="docutils literal notranslate"><span class="pre">ssl._https_verify_envvar</span></code>: environment based configuration</li>
<li><code class="docutils literal notranslate"><span class="pre">ssl._cert_verification_config</span></code>: file based configuration (<a class="pep reference internal" href="../pep-0476/" title="PEP 476 Enabling certificate verification by default for stdlib http clients">PEP 476</a> opt-in)</li>
</ul>
<p>The marker attributes are prefixed with an underscore to indicate the
implementation dependent and security sensitive nature of these capabilities.</p>
</section>
<section id="feature-configuration-api">
<h2><a class="toc-backref" href="#feature-configuration-api" role="doc-backlink">Feature: Configuration API</a></h2>
<p>This change is proposed for inclusion in CPython 2.7.12 and later CPython 2.7.x
releases. It consists of a new <code class="docutils literal notranslate"><span class="pre">ssl._https_verify_certificates()</span></code> to specify
the default handling of HTTPS certificates in standard library client libraries.</p>
<p>It is not proposed to forward port this change to Python 3, so Python 3
applications that need to support skipping certificate verification will still
need to define their own suitable security context.</p>
<section id="feature-detection">
<h3><a class="toc-backref" href="#feature-detection" role="doc-backlink">Feature detection</a></h3>
<p>The marker attribute on the <code class="docutils literal notranslate"><span class="pre">ssl</span></code> module related to this feature is the
<code class="docutils literal notranslate"><span class="pre">ssl._https_verify_certificates</span></code> function itself.</p>
</section>
<section id="specification">
<h3><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h3>
<p>The <code class="docutils literal notranslate"><span class="pre">ssl._https_verify_certificates</span></code> function will work as follows:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">_https_verify_certificates</span><span class="p">(</span><span class="n">enable</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Verify server HTTPS certificates by default?&quot;&quot;&quot;</span>
<span class="k">global</span> <span class="n">_create_default_https_context</span>
<span class="k">if</span> <span class="n">enable</span><span class="p">:</span>
<span class="n">_create_default_https_context</span> <span class="o">=</span> <span class="n">create_default_context</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">_create_default_https_context</span> <span class="o">=</span> <span class="n">_create_unverified_context</span>
</pre></div>
</div>
<p>If called without arguments, or with <code class="docutils literal notranslate"><span class="pre">enable</span></code> set to a true value, then
standard library client modules will subsequently verify HTTPS certificates by default, otherwise they will skip verification.</p>
<p>If called with <code class="docutils literal notranslate"><span class="pre">enable</span></code> set to a false value, then standard library client
modules will subsequently skip verifying HTTPS certificates by default.</p>
</section>
<section id="security-considerations">
<h3><a class="toc-backref" href="#security-considerations" role="doc-backlink">Security Considerations</a></h3>
<p>The inclusion of this feature will allow security sensitive applications to
include the following forward-compatible snippet in their code:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">ssl</span><span class="p">,</span> <span class="s2">&quot;_https_verify_certificates&quot;</span><span class="p">):</span>
<span class="n">ssl</span><span class="o">.</span><span class="n">_https_verify_certificates</span><span class="p">()</span>
</pre></div>
</div>
<p>Some developers may also choose to opt out of certificate checking using
<code class="docutils literal notranslate"><span class="pre">ssl._https_verify_certificates(enable=False)</span></code>. This doesnt introduce any
major new security concerns, as monkeypatching the affected internal APIs was
already possible.</p>
</section>
</section>
<section id="feature-environment-based-configuration">
<h2><a class="toc-backref" href="#feature-environment-based-configuration" role="doc-backlink">Feature: environment based configuration</a></h2>
<p>This change is proposed for inclusion in CPython 2.7.12 and later CPython 2.7.x
releases. It consists of a new <code class="docutils literal notranslate"><span class="pre">PYTHONHTTPSVERIFY</span></code> environment variable that
can be set to <code class="docutils literal notranslate"><span class="pre">'0'</span></code> to disable the default verification without modifying the
application source code (which may not even be available in cases of
bytecode-only application distribution)</p>
<p>It is not proposed to forward port this change to Python 3, so Python 3
applications that need to support skipping certificate verification will still
need to define their own suitable security context.</p>
<section id="id1">
<h3><a class="toc-backref" href="#id1" role="doc-backlink">Feature detection</a></h3>
<p>The marker attribute on the <code class="docutils literal notranslate"><span class="pre">ssl</span></code> module related to this feature is:</p>
<ul class="simple">
<li>the <code class="docutils literal notranslate"><span class="pre">ssl._https_verify_envvar</span></code> attribute, giving the name of environment
variable affecting the default behaviour</li>
</ul>
<p>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.</p>
</section>
<section id="id2">
<h3><a class="toc-backref" href="#id2" role="doc-backlink">Specification</a></h3>
<p>Rather than always defaulting to the use of <code class="docutils literal notranslate"><span class="pre">ssl.create_default_context</span></code>,
the <code class="docutils literal notranslate"><span class="pre">ssl</span></code> module will be modified to:</p>
<ul class="simple">
<li>read the <code class="docutils literal notranslate"><span class="pre">PYTHONHTTPSVERIFY</span></code> environment variable when the module is first
imported into a Python process</li>
<li>set the <code class="docutils literal notranslate"><span class="pre">ssl._create_default_https_context</span></code> function to be an alias for
<code class="docutils literal notranslate"><span class="pre">ssl._create_unverified_context</span></code> if this environment variable is present
and set to <code class="docutils literal notranslate"><span class="pre">'0'</span></code></li>
<li>otherwise, set the <code class="docutils literal notranslate"><span class="pre">ssl._create_default_https_context</span></code> function to be an
alias for <code class="docutils literal notranslate"><span class="pre">ssl.create_default_context</span></code> as usual</li>
</ul>
</section>
<section id="example-implementation">
<h3><a class="toc-backref" href="#example-implementation" role="doc-backlink">Example implementation</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_https_verify_envvar</span> <span class="o">=</span> <span class="s1">&#39;PYTHONHTTPSVERIFY&#39;</span>
<span class="k">def</span> <span class="nf">_get_https_context_factory</span><span class="p">():</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">sys</span><span class="o">.</span><span class="n">flags</span><span class="o">.</span><span class="n">ignore_environment</span><span class="p">:</span>
<span class="n">config_setting</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">_https_verify_envvar</span><span class="p">)</span>
<span class="k">if</span> <span class="n">config_setting</span> <span class="o">==</span> <span class="s1">&#39;0&#39;</span><span class="p">:</span>
<span class="k">return</span> <span class="n">_create_unverified_context</span>
<span class="k">return</span> <span class="n">create_default_context</span>
<span class="n">_create_default_https_context</span> <span class="o">=</span> <span class="n">_get_https_context_factory</span><span class="p">()</span>
</pre></div>
</div>
</section>
<section id="id3">
<h3><a class="toc-backref" href="#id3" role="doc-backlink">Security Considerations</a></h3>
<p>Relative to the behaviour in Python 3.4.3+ and Python 2.7.9-&gt;2.7.11, this
approach does introduce a new downgrade attack against the default security
settings that potentially allows a sufficiently determined attacker to revert
Python to the default behaviour used in CPython 2.7.8 and earlier releases.</p>
<p>This slight increase in the available attack surface is a key reason why:</p>
<ul class="simple">
<li>security sensitive applications should still define their own SSL context</li>
<li>the migration features described in this PEP are not being added to Python 3</li>
</ul>
<p>However, its also worth keeping in mind that carrying out such an attack
requires the ability to modify the execution environment of a Python process
prior to the import of the <code class="docutils literal notranslate"><span class="pre">ssl</span></code> module. In combination with the ability
to write to any part of the filesystem (such as <code class="docutils literal notranslate"><span class="pre">/tmp</span></code>), any attacker with
such access would already be able to modify the behaviour of the underlying
OpenSSL implementation, the dynamic library loader, and other potentially
security sensitive components.</p>
</section>
<section id="interaction-with-python-virtual-environments">
<h3><a class="toc-backref" href="#interaction-with-python-virtual-environments" role="doc-backlink">Interaction with Python virtual environments</a></h3>
<p>The default setting is read directly from the process environment, and hence
works the same way regardless of whether or not the interpreter is being run
inside an activated Python virtual environment.</p>
</section>
</section>
<section id="reference-implementation">
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
<p>A patch for Python 2.7 implementing the above two features is attached to
the <a class="reference external" href="http://bugs.python.org/issue23857">relevant tracker issue</a>.</p>
</section>
<section id="backporting-this-pep-to-earlier-python-versions">
<h2><a class="toc-backref" href="#backporting-this-pep-to-earlier-python-versions" role="doc-backlink">Backporting this PEP to earlier Python versions</a></h2>
<p>If this PEP is accepted, then commercial Python redistributors may choose to
backport the per-process configuration mechanisms defined in this PEP to base
versions older than Python 2.7.9, <em>without</em> also backporting <a class="pep reference internal" href="../pep-0476/" title="PEP 476 Enabling certificate verification by default for stdlib http clients">PEP 476</a>s change
to the default behaviour of the overall Python installation.</p>
<p>Such a backport would differ from the mechanism proposed in this PEP solely in
the default behaviour when <code class="docutils literal notranslate"><span class="pre">PYTHONHTTPSVERIFY</span></code> was not set at all: it would
continue to default to skipping certificate validation.</p>
<p>In this case, if the <code class="docutils literal notranslate"><span class="pre">PYTHONHTTPSVERIFY</span></code> environment variable is defined, and
set to anything <em>other</em> than <code class="docutils literal notranslate"><span class="pre">'0'</span></code>, then HTTPS certificate verification
should be enabled.</p>
<section id="id4">
<h3><a class="toc-backref" href="#id4" role="doc-backlink">Feature detection</a></h3>
<p>Theres no specific attribute indicating that this situation applies. Rather,
it is indicated by the <code class="docutils literal notranslate"><span class="pre">ssl._https_verify_certificates</span></code> and
<code class="docutils literal notranslate"><span class="pre">ssl._https_verify_envvar</span></code> attributes being present in a Python version that
is nominally older than Python 2.7.12.</p>
</section>
<section id="id5">
<h3><a class="toc-backref" href="#id5" role="doc-backlink">Specification</a></h3>
<p>Implementing this backport involves backporting the changes in <a class="pep reference internal" href="../pep-0466/" title="PEP 466 Network Security Enhancements for Python 2.7.x">PEP 466</a>, 476 and
this PEP, with the following change to the handling of the
<code class="docutils literal notranslate"><span class="pre">PYTHONHTTPSVERIFY</span></code> environment variable in the <code class="docutils literal notranslate"><span class="pre">ssl</span></code> module:</p>
<ul class="simple">
<li>read the <code class="docutils literal notranslate"><span class="pre">PYTHONHTTPSVERIFY</span></code> environment variable when the module is first
imported into a Python process</li>
<li>set the <code class="docutils literal notranslate"><span class="pre">ssl._create_default_https_context</span></code> function to be an alias for
<code class="docutils literal notranslate"><span class="pre">ssl.create_default_context</span></code> if this environment variable is present
and set to any value other than <code class="docutils literal notranslate"><span class="pre">'0'</span></code></li>
<li>otherwise, set the <code class="docutils literal notranslate"><span class="pre">ssl._create_default_https_context</span></code> function to be an
alias for <code class="docutils literal notranslate"><span class="pre">ssl._create_unverified_context</span></code></li>
</ul>
</section>
<section id="id6">
<h3><a class="toc-backref" href="#id6" role="doc-backlink">Example implementation</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_https_verify_envvar</span> <span class="o">=</span> <span class="s1">&#39;PYTHONHTTPSVERIFY&#39;</span>
<span class="k">def</span> <span class="nf">_get_https_context_factory</span><span class="p">():</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">sys</span><span class="o">.</span><span class="n">flags</span><span class="o">.</span><span class="n">ignore_environment</span><span class="p">:</span>
<span class="n">config_setting</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">_https_verify_envvar</span><span class="p">)</span>
<span class="k">if</span> <span class="n">config_setting</span> <span class="o">!=</span> <span class="s1">&#39;0&#39;</span><span class="p">:</span>
<span class="k">return</span> <span class="n">create_default_context</span>
<span class="k">return</span> <span class="n">_create_unverified_context</span>
<span class="n">_create_default_https_context</span> <span class="o">=</span> <span class="n">_get_https_context_factory</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">_disable_https_default_verification</span><span class="p">():</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Skip verification of HTTPS certificates by default&quot;&quot;&quot;</span>
<span class="k">global</span> <span class="n">_create_default_https_context</span>
<span class="n">_create_default_https_context</span> <span class="o">=</span> <span class="n">_create_unverified_context</span>
</pre></div>
</div>
</section>
<section id="id7">
<h3><a class="toc-backref" href="#id7" role="doc-backlink">Security Considerations</a></h3>
<p>This change would be a strict security upgrade for any Python version that
currently defaults to skipping certificate validation in standard library
HTTPS clients. The technical trade-offs to be taken into account relate largely
to the magnitude of the <a class="pep reference internal" href="../pep-0466/" title="PEP 466 Network Security Enhancements for Python 2.7.x">PEP 466</a> backport also required rather than to anything
security related.</p>
</section>
<section id="id8">
<h3><a class="toc-backref" href="#id8" role="doc-backlink">Interaction with Python virtual environments</a></h3>
<p>The default setting is read directly from the process environment, and hence
works the same way regardless of whether or not the interpreter is being run
inside an activated Python virtual environment.</p>
</section>
</section>
<section id="backporting-pep-476-to-earlier-python-versions">
<h2><a class="toc-backref" href="#backporting-pep-476-to-earlier-python-versions" role="doc-backlink">Backporting PEP 476 to earlier Python versions</a></h2>
<p>The backporting approach described above leaves the default HTTPS certificate
verification behaviour of a Python 2.7 installation unmodified: verifying
certificates still needs to be opted into on a per-connection or per-process
basis.</p>
<p>To allow the default behaviour of the entire installation to be modified
without breaking backwards compatibility, Red Hat designed a configuration
mechanism for the system Python 2.7 installation in Red Hat Enterprise Linux
7.2+ that provides:</p>
<ul class="simple">
<li>an opt-in model that allows the decision to enable HTTPS certificate
verification to be made independently of the decision to upgrade to the
operating system version where the feature was first backported</li>
<li>the ability for system administrators to set the default behaviour of Python
applications and scripts run directly in the system Python installation</li>
<li>the ability for the redistributor to consider changing the default behaviour
of <em>new</em> installations at some point in the future without impacting existing
installations that have been explicitly configured to skip verifying HTTPS
certificates by default</li>
</ul>
<p>As it only affects backports to earlier releases of Python 2.7, this change is
not proposed for inclusion in upstream CPython, but rather is offered as
a recommendation to other redistributors that choose to offer a similar feature
to their users.</p>
<p>This PEP doesnt take a position on whether or not this particular change is a
good idea - rather, it suggests that <em>if</em> a redistributor chooses to go down
the path of making the default behaviour configurable in a version of Python
older than Python 2.7.9, then maintaining a consistent approach across
redistributors would be beneficial for users.</p>
<p>However, this approach SHOULD NOT be used for any Python installation that
advertises itself as providing Python 2.7.9 or later, as most Python users
will have the reasonable expectation that all such environments will verify
HTTPS certificates by default.</p>
<section id="id9">
<h3><a class="toc-backref" href="#id9" role="doc-backlink">Feature detection</a></h3>
<p>The marker attribute on the <code class="docutils literal notranslate"><span class="pre">ssl</span></code> module related to this feature is:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_cert_verification_config</span> <span class="o">=</span> <span class="s1">&#39;&lt;path to configuration file&gt;&#39;</span>
</pre></div>
</div>
<p>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.</p>
</section>
<section id="recommended-modifications-to-the-python-standard-library">
<h3><a class="toc-backref" href="#recommended-modifications-to-the-python-standard-library" role="doc-backlink">Recommended modifications to the Python standard library</a></h3>
<p>The recommended approach to backporting the <a class="pep reference internal" href="../pep-0476/" title="PEP 476 Enabling certificate verification by default for stdlib http clients">PEP 476</a> modifications to an earlier
point release is to implement the following changes relative to the default
<a class="pep reference internal" href="../pep-0476/" title="PEP 476 Enabling certificate verification by default for stdlib http clients">PEP 476</a> behaviour implemented in Python 2.7.9+:</p>
<ul class="simple">
<li>modify the <code class="docutils literal notranslate"><span class="pre">ssl</span></code> module to read a system wide configuration file when the
module is first imported into a Python process</li>
<li>define a platform default behaviour (either verifying or not verifying HTTPS
certificates) to be used if this configuration file is not present</li>
<li>support selection between the following three modes of operation:<ul>
<li>ensure HTTPS certificate verification is enabled</li>
<li>ensure HTTPS certificate verification is disabled</li>
<li>delegate the decision to the redistributor providing this Python version</li>
</ul>
</li>
<li>set the <code class="docutils literal notranslate"><span class="pre">ssl._create_default_https_context</span></code> function to be an alias for
either <code class="docutils literal notranslate"><span class="pre">ssl.create_default_context</span></code> or <code class="docutils literal notranslate"><span class="pre">ssl._create_unverified_context</span></code>
based on the given configuration setting.</li>
</ul>
</section>
<section id="recommended-file-location">
<h3><a class="toc-backref" href="#recommended-file-location" role="doc-backlink">Recommended file location</a></h3>
<p>As the PEP authors are not aware of any vendors providing long-term support
releases targeting Windows, Mac OS X or *BSD systems, this approach is
currently only specifically defined for Linux system Python installations.</p>
<p>The recommended configuration file name on Linux systems is
<code class="docutils literal notranslate"><span class="pre">/etc/python/cert-verification.cfg</span></code>.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">.cfg</span></code> filename extension is recommended for consistency with the
<code class="docutils literal notranslate"><span class="pre">pyvenv.cfg</span></code> used by the <code class="docutils literal notranslate"><span class="pre">venv</span></code> module in Python 3s standard library.</p>
</section>
<section id="recommended-file-format">
<h3><a class="toc-backref" href="#recommended-file-format" role="doc-backlink">Recommended file format</a></h3>
<p>The configuration file should use a ConfigParser ini-style format with a
single section named <code class="docutils literal notranslate"><span class="pre">[https]</span></code> containing one required setting <code class="docutils literal notranslate"><span class="pre">verify</span></code>.</p>
<p>The suggested section name is taken from the “https” URL schema passed to
affected client APIs.</p>
<p>Permitted values for <code class="docutils literal notranslate"><span class="pre">verify</span></code> are:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">enable</span></code>: ensure HTTPS certificate verification is enabled by default</li>
<li><code class="docutils literal notranslate"><span class="pre">disable</span></code>: ensure HTTPS certificate verification is disabled by default</li>
<li><code class="docutils literal notranslate"><span class="pre">platform_default</span></code>: delegate the decision to the redistributor providing
this particular Python version</li>
</ul>
<p>If the <code class="docutils literal notranslate"><span class="pre">[https]</span></code> section or the <code class="docutils literal notranslate"><span class="pre">verify</span></code> setting are missing, or if the
<code class="docutils literal notranslate"><span class="pre">verify</span></code> setting is set to an unknown value, it should be treated as if the
configuration file is not present.</p>
</section>
<section id="id10">
<h3><a class="toc-backref" href="#id10" role="doc-backlink">Example implementation</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_cert_verification_config</span> <span class="o">=</span> <span class="s1">&#39;/etc/python/cert-verification.cfg&#39;</span>
<span class="k">def</span> <span class="nf">_get_https_context_factory</span><span class="p">():</span>
<span class="c1"># Check for a system-wide override of the default behaviour</span>
<span class="n">context_factories</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">&#39;enable&#39;</span><span class="p">:</span> <span class="n">create_default_context</span><span class="p">,</span>
<span class="s1">&#39;disable&#39;</span><span class="p">:</span> <span class="n">_create_unverified_context</span><span class="p">,</span>
<span class="s1">&#39;platform_default&#39;</span><span class="p">:</span> <span class="n">_create_unverified_context</span><span class="p">,</span> <span class="c1"># For now :)</span>
<span class="p">}</span>
<span class="kn">import</span> <span class="nn">ConfigParser</span>
<span class="n">config</span> <span class="o">=</span> <span class="n">ConfigParser</span><span class="o">.</span><span class="n">RawConfigParser</span><span class="p">()</span>
<span class="n">config</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">_cert_verification_config</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">verify_mode</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;https&#39;</span><span class="p">,</span> <span class="s1">&#39;verify&#39;</span><span class="p">)</span>
<span class="k">except</span> <span class="p">(</span><span class="n">ConfigParser</span><span class="o">.</span><span class="n">NoSectionError</span><span class="p">,</span> <span class="n">ConfigParser</span><span class="o">.</span><span class="n">NoOptionError</span><span class="p">):</span>
<span class="n">verify_mode</span> <span class="o">=</span> <span class="s1">&#39;platform_default&#39;</span>
<span class="n">default_factory</span> <span class="o">=</span> <span class="n">context_factories</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;platform_default&#39;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">context_factories</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">verify_mode</span><span class="p">,</span> <span class="n">default_factory</span><span class="p">)</span>
<span class="n">_create_default_https_context</span> <span class="o">=</span> <span class="n">_get_https_context_factory</span><span class="p">()</span>
</pre></div>
</div>
</section>
<section id="id11">
<h3><a class="toc-backref" href="#id11" role="doc-backlink">Security Considerations</a></h3>
<p>The specific recommendations for this backporting case are designed to work for
privileged, security sensitive processes, even those being run in the following
locked down configuration:</p>
<ul class="simple">
<li>run from a locked down administrator controlled directory rather than a normal
user directory (preventing <code class="docutils literal notranslate"><span class="pre">sys.path[0]</span></code> based privilege escalation attacks)</li>
<li>run using the <code class="docutils literal notranslate"><span class="pre">-E</span></code> switch (preventing <code class="docutils literal notranslate"><span class="pre">PYTHON*</span></code> environment variable based
privilege escalation attacks)</li>
<li>run using the <code class="docutils literal notranslate"><span class="pre">-s</span></code> switch (preventing user site directory based privilege
escalation attacks)</li>
<li>run using the <code class="docutils literal notranslate"><span class="pre">-S</span></code> switch (preventing <code class="docutils literal notranslate"><span class="pre">sitecustomize</span></code> based privilege
escalation attacks)</li>
</ul>
<p>The intent is that the <em>only</em> reason HTTPS verification should be getting
turned off installation wide when using this approach is because:</p>
<ul class="simple">
<li>an end user is running a redistributor provided version of CPython rather
than running upstream CPython directly</li>
<li>that redistributor has decided to provide a smoother migration path to
verifying HTTPS certificates by default than that being provided by the
upstream project</li>
<li>either the redistributor or the local infrastructure administrator has
determined that it is appropriate to retain the default pre-2.7.9 behaviour
(at least for the time being)</li>
</ul>
<p>Using an administrator controlled configuration file rather than an environment
variable has the essential feature of providing a smoother migration path, even
for applications being run with the <code class="docutils literal notranslate"><span class="pre">-E</span></code> switch.</p>
</section>
<section id="id12">
<h3><a class="toc-backref" href="#id12" role="doc-backlink">Interaction with Python virtual environments</a></h3>
<p>This setting is scoped by the interpreter installation and affects all Python
processes using that interpreter, regardless of whether or not the interpreter
is being run inside an activated Python virtual environment.</p>
</section>
<section id="origins-of-this-recommendation">
<h3><a class="toc-backref" href="#origins-of-this-recommendation" role="doc-backlink">Origins of this recommendation</a></h3>
<p>This recommendation is based on the backporting approach adopted for Red Hat
Enterprise Linux 7.2, as published in the original July 2015 draft of this PEP
and described in detail in <a class="reference external" href="https://access.redhat.com/articles/2039753">this KnowledgeBase article</a>. Red Hats patches implementing
this backport for Python 2.7.5 can be found in the <a class="reference external" href="https://git.centos.org/commit/rpms!python.git/refs!heads!c7">CentOS git repository</a>.</p>
</section>
</section>
<section id="recommendation-for-combined-feature-backports">
<h2><a class="toc-backref" href="#recommendation-for-combined-feature-backports" role="doc-backlink">Recommendation for combined feature backports</a></h2>
<p>If a redistributor chooses to backport the environment variable based
configuration setting from this PEP to a modified Python version that also
implements the configuration file based <a class="pep reference internal" href="../pep-0476/" title="PEP 476 Enabling certificate verification by default for stdlib http clients">PEP 476</a> backport, then the environment
variable should take precedence over the system-wide configuration setting.
This allows the setting to be changed for a given user or application,
regardless of the installation-wide default behaviour.</p>
<section id="id13">
<h3><a class="toc-backref" href="#id13" role="doc-backlink">Example implementation</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_https_verify_envvar</span> <span class="o">=</span> <span class="s1">&#39;PYTHONHTTPSVERIFY&#39;</span>
<span class="n">_cert_verification_config</span> <span class="o">=</span> <span class="s1">&#39;/etc/python/cert-verification.cfg&#39;</span>
<span class="k">def</span> <span class="nf">_get_https_context_factory</span><span class="p">():</span>
<span class="c1"># Check for an environmental override of the default behaviour</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">sys</span><span class="o">.</span><span class="n">flags</span><span class="o">.</span><span class="n">ignore_environment</span><span class="p">:</span>
<span class="n">config_setting</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">_https_verify_envvar</span><span class="p">)</span>
<span class="k">if</span> <span class="n">config_setting</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">if</span> <span class="n">config_setting</span> <span class="o">==</span> <span class="s1">&#39;0&#39;</span><span class="p">:</span>
<span class="k">return</span> <span class="n">_create_unverified_context</span>
<span class="k">return</span> <span class="n">create_default_context</span>
<span class="c1"># Check for a system-wide override of the default behaviour</span>
<span class="n">context_factories</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">&#39;enable&#39;</span><span class="p">:</span> <span class="n">create_default_context</span><span class="p">,</span>
<span class="s1">&#39;disable&#39;</span><span class="p">:</span> <span class="n">_create_unverified_context</span><span class="p">,</span>
<span class="s1">&#39;platform_default&#39;</span><span class="p">:</span> <span class="n">_create_unverified_context</span><span class="p">,</span> <span class="c1"># For now :)</span>
<span class="p">}</span>
<span class="kn">import</span> <span class="nn">ConfigParser</span>
<span class="n">config</span> <span class="o">=</span> <span class="n">ConfigParser</span><span class="o">.</span><span class="n">RawConfigParser</span><span class="p">()</span>
<span class="n">config</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">_cert_verification_config</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">verify_mode</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;https&#39;</span><span class="p">,</span> <span class="s1">&#39;verify&#39;</span><span class="p">)</span>
<span class="k">except</span> <span class="p">(</span><span class="n">ConfigParser</span><span class="o">.</span><span class="n">NoSectionError</span><span class="p">,</span> <span class="n">ConfigParser</span><span class="o">.</span><span class="n">NoOptionError</span><span class="p">):</span>
<span class="n">verify_mode</span> <span class="o">=</span> <span class="s1">&#39;platform_default&#39;</span>
<span class="n">default_factory</span> <span class="o">=</span> <span class="n">context_factories</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;platform_default&#39;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">context_factories</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">verify_mode</span><span class="p">,</span> <span class="n">default_factory</span><span class="p">)</span>
<span class="n">_create_default_https_context</span> <span class="o">=</span> <span class="n">_get_https_context_factory</span><span class="p">()</span>
</pre></div>
</div>
</section>
</section>
<section id="copyright">
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
<p>This document has been placed into the public domain.</p>
</section>
</section>
<hr class="docutils" />
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0493.rst">https://github.com/python/peps/blob/main/peps/pep-0493.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0493.rst">2023-10-11 12:05:51 GMT</a></p>
</article>
<nav id="pep-sidebar">
<h2>Contents</h2>
<ul>
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a><ul>
<li><a class="reference internal" href="#alternatives">Alternatives</a></li>
</ul>
</li>
<li><a class="reference internal" href="#scope-limitations">Scope Limitations</a></li>
<li><a class="reference internal" href="#requirements-for-capability-detection">Requirements for capability detection</a></li>
<li><a class="reference internal" href="#feature-configuration-api">Feature: Configuration API</a><ul>
<li><a class="reference internal" href="#feature-detection">Feature detection</a></li>
<li><a class="reference internal" href="#specification">Specification</a></li>
<li><a class="reference internal" href="#security-considerations">Security Considerations</a></li>
</ul>
</li>
<li><a class="reference internal" href="#feature-environment-based-configuration">Feature: environment based configuration</a><ul>
<li><a class="reference internal" href="#id1">Feature detection</a></li>
<li><a class="reference internal" href="#id2">Specification</a></li>
<li><a class="reference internal" href="#example-implementation">Example implementation</a></li>
<li><a class="reference internal" href="#id3">Security Considerations</a></li>
<li><a class="reference internal" href="#interaction-with-python-virtual-environments">Interaction with Python virtual environments</a></li>
</ul>
</li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#backporting-this-pep-to-earlier-python-versions">Backporting this PEP to earlier Python versions</a><ul>
<li><a class="reference internal" href="#id4">Feature detection</a></li>
<li><a class="reference internal" href="#id5">Specification</a></li>
<li><a class="reference internal" href="#id6">Example implementation</a></li>
<li><a class="reference internal" href="#id7">Security Considerations</a></li>
<li><a class="reference internal" href="#id8">Interaction with Python virtual environments</a></li>
</ul>
</li>
<li><a class="reference internal" href="#backporting-pep-476-to-earlier-python-versions">Backporting PEP 476 to earlier Python versions</a><ul>
<li><a class="reference internal" href="#id9">Feature detection</a></li>
<li><a class="reference internal" href="#recommended-modifications-to-the-python-standard-library">Recommended modifications to the Python standard library</a></li>
<li><a class="reference internal" href="#recommended-file-location">Recommended file location</a></li>
<li><a class="reference internal" href="#recommended-file-format">Recommended file format</a></li>
<li><a class="reference internal" href="#id10">Example implementation</a></li>
<li><a class="reference internal" href="#id11">Security Considerations</a></li>
<li><a class="reference internal" href="#id12">Interaction with Python virtual environments</a></li>
<li><a class="reference internal" href="#origins-of-this-recommendation">Origins of this recommendation</a></li>
</ul>
</li>
<li><a class="reference internal" href="#recommendation-for-combined-feature-backports">Recommendation for combined feature backports</a><ul>
<li><a class="reference internal" href="#id13">Example implementation</a></li>
</ul>
</li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
<br>
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0493.rst">Page Source (GitHub)</a>
</nav>
</section>
<script src="../_static/colour_scheme.js"></script>
<script src="../_static/wrap_tables.js"></script>
<script src="../_static/sticky_banner.js"></script>
</body>
</html>