755 lines
56 KiB
HTML
755 lines
56 KiB
HTML
|
||
<!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 578 – Python Runtime Audit Hooks | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0578/">
|
||
<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 578 – Python Runtime Audit Hooks | peps.python.org'>
|
||
<meta property="og:description" content="This PEP describes additions to the Python API and specific behaviors for the CPython implementation that make actions taken by the Python runtime visible to auditing tools. Visibility into these actions provides opportunities for test frameworks, loggi...">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0578/">
|
||
<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="This PEP describes additions to the Python API and specific behaviors for the CPython implementation that make actions taken by the Python runtime visible to auditing tools. Visibility into these actions provides opportunities for test frameworks, loggi...">
|
||
<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> » </li>
|
||
<li><a href="../pep-0000/">PEP Index</a> » </li>
|
||
<li>PEP 578</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 578 – Python Runtime Audit Hooks</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Steve Dower <steve.dower at python.org></dd>
|
||
<dt class="field-even">BDFL-Delegate<span class="colon">:</span></dt>
|
||
<dd class="field-even">Christian Heimes <christian at python.org></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">16-Jun-2018</dd>
|
||
<dt class="field-even">Python-Version<span class="colon">:</span></dt>
|
||
<dd class="field-even">3.8</dd>
|
||
<dt class="field-odd">Post-History<span class="colon">:</span></dt>
|
||
<dd class="field-odd">28-Mar-2019, 07-May-2019</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="#background">Background</a></li>
|
||
<li><a class="reference internal" href="#overview-of-changes">Overview of Changes</a><ul>
|
||
<li><a class="reference internal" href="#audit-hook">Audit Hook</a></li>
|
||
<li><a class="reference internal" href="#verified-open-hook">Verified Open Hook</a></li>
|
||
<li><a class="reference internal" href="#api-availability">API Availability</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#suggested-audit-hook-locations">Suggested Audit Hook Locations</a></li>
|
||
<li><a class="reference internal" href="#performance-impact">Performance Impact</a></li>
|
||
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
|
||
<li><a class="reference internal" href="#separate-module-for-audit-hooks">Separate module for audit hooks</a></li>
|
||
<li><a class="reference internal" href="#flag-in-sys-flags-to-indicate-audited-mode">Flag in sys.flags to indicate “audited” mode</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#why-not-a-sandbox">Why Not A Sandbox</a></li>
|
||
<li><a class="reference internal" href="#relationship-to-pep-551">Relationship to PEP 551</a></li>
|
||
<li><a class="reference internal" href="#references">References</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
</details></section>
|
||
<div class="pep-banner canonical-doc sticky-banner admonition important">
|
||
<p class="admonition-title">Important</p>
|
||
<p>This PEP is a historical document. The up-to-date, canonical documentation can now be found at <a class="reference external" href="https://docs.python.org/3/library/audit_events.html#audit-events" title="(in Python v3.13)"><span>Audit events table</span></a>.</p>
|
||
<p class="close-button">×</p>
|
||
<p>See <a class="pep reference internal" href="../pep-0001/" title="PEP 1 – PEP Purpose and Guidelines">PEP 1</a> for how to propose changes.</p>
|
||
</div>
|
||
<section id="abstract">
|
||
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
|
||
<p>This PEP describes additions to the Python API and specific behaviors
|
||
for the CPython implementation that make actions taken by the Python
|
||
runtime visible to auditing tools. Visibility into these actions
|
||
provides opportunities for test frameworks, logging frameworks, and
|
||
security tools to monitor and optionally limit actions taken by the
|
||
runtime.</p>
|
||
<p>This PEP proposes adding two APIs to provide insights into a running
|
||
Python application: one for arbitrary events, and another specific to
|
||
the module import system. The APIs are intended to be available in all
|
||
Python implementations, though the specific messages and values used
|
||
are unspecified here to allow implementations the freedom to determine
|
||
how best to provide information to their users. Some examples likely
|
||
to be used in CPython are provided for explanatory purposes.</p>
|
||
<p>See <a class="pep reference internal" href="../pep-0551/" title="PEP 551 – Security transparency in the Python runtime">PEP 551</a> for discussion and recommendations on enhancing the
|
||
security of a Python runtime making use of these auditing APIs.</p>
|
||
</section>
|
||
<section id="background">
|
||
<h2><a class="toc-backref" href="#background" role="doc-backlink">Background</a></h2>
|
||
<p>Python provides access to a wide range of low-level functionality on
|
||
many common operating systems. While this is incredibly useful for
|
||
“write-once, run-anywhere” scripting, it also makes monitoring of
|
||
software written in Python difficult. Because Python uses native system
|
||
APIs directly, existing monitoring tools either suffer from limited
|
||
context or auditing bypass.</p>
|
||
<p>Limited context occurs when system monitoring can report that an
|
||
action occurred, but cannot explain the sequence of events leading to
|
||
it. For example, network monitoring at the OS level may be able to
|
||
report “listening started on port 5678”, but may not be able to
|
||
provide the process ID, command line, parent process, or the local
|
||
state in the program at the point that triggered the action. Firewall
|
||
controls to prevent such an action are similarly limited, typically
|
||
to process names or some global state such as the current user, and
|
||
in any case rarely provide a useful log file correlated with other
|
||
application messages.</p>
|
||
<p>Auditing bypass can occur when the typical system tool used for an
|
||
action would ordinarily report its use, but accessing the APIs via
|
||
Python do not trigger this. For example, invoking “curl” to make HTTP
|
||
requests may be specifically monitored in an audited system, but
|
||
Python’s “urlretrieve” function is not.</p>
|
||
<p>Within a long-running Python application, particularly one that
|
||
processes user-provided information such as a web app, there is a risk
|
||
of unexpected behavior. This may be due to bugs in the code, or
|
||
deliberately induced by a malicious user. In both cases, normal
|
||
application logging may be bypassed resulting in no indication that
|
||
anything out of the ordinary has occurred.</p>
|
||
<p>Additionally, and somewhat unique to Python, it is very easy to affect
|
||
the code that is run in an application by manipulating either the
|
||
import system’s search path or placing files earlier on the path than
|
||
intended. This is often seen when developers create a script with the
|
||
same name as the module they intend to use - for example, a
|
||
<code class="docutils literal notranslate"><span class="pre">random.py</span></code> file that attempts to import the standard library
|
||
<code class="docutils literal notranslate"><span class="pre">random</span></code> module.</p>
|
||
<p>This is not sandboxing, as this proposal does not attempt to prevent
|
||
malicious behavior (though it enables some new options to do so).
|
||
See the <a class="reference internal" href="#why-not-a-sandbox">Why Not A Sandbox</a> section below for further discussion.</p>
|
||
</section>
|
||
<section id="overview-of-changes">
|
||
<h2><a class="toc-backref" href="#overview-of-changes" role="doc-backlink">Overview of Changes</a></h2>
|
||
<p>The aim of these changes is to enable both application developers and
|
||
system administrators to integrate Python into their existing
|
||
monitoring systems without dictating how those systems look or behave.</p>
|
||
<p>We propose two API changes to enable this: an Audit Hook and Verified
|
||
Open Hook. Both are available from Python and native code, allowing
|
||
applications and frameworks written in pure Python code to take
|
||
advantage of the extra messages, while also allowing embedders or
|
||
system administrators to deploy builds of Python where auditing is
|
||
always enabled.</p>
|
||
<p>Only CPython is bound to provide the native APIs as described here.
|
||
Other implementations should provide the pure Python APIs, and
|
||
may provide native versions as appropriate for their underlying
|
||
runtimes. Auditing events are likewise considered implementation
|
||
specific, but are bound by normal feature compatibility guarantees.</p>
|
||
<section id="audit-hook">
|
||
<h3><a class="toc-backref" href="#audit-hook" role="doc-backlink">Audit Hook</a></h3>
|
||
<p>In order to observe actions taken by the runtime (on behalf of the
|
||
caller), an API is required to raise messages from within certain
|
||
operations. These operations are typically deep within the Python
|
||
runtime or standard library, such as dynamic code compilation, module
|
||
imports, DNS resolution, or use of certain modules such as <code class="docutils literal notranslate"><span class="pre">ctypes</span></code>.</p>
|
||
<p>The following new C APIs allow embedders and CPython implementors to
|
||
send and receive audit hook messages:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Add an auditing hook</span>
|
||
<span class="n">typedef</span> <span class="nb">int</span> <span class="p">(</span><span class="o">*</span><span class="n">hook_func</span><span class="p">)(</span><span class="n">const</span> <span class="n">char</span> <span class="o">*</span><span class="n">event</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span>
|
||
<span class="n">void</span> <span class="o">*</span><span class="n">userData</span><span class="p">);</span>
|
||
<span class="nb">int</span> <span class="n">PySys_AddAuditHook</span><span class="p">(</span><span class="n">hook_func</span> <span class="n">hook</span><span class="p">,</span> <span class="n">void</span> <span class="o">*</span><span class="n">userData</span><span class="p">);</span>
|
||
|
||
<span class="c1"># Raise an event with all auditing hooks</span>
|
||
<span class="nb">int</span> <span class="n">PySys_Audit</span><span class="p">(</span><span class="n">const</span> <span class="n">char</span> <span class="o">*</span><span class="n">event</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">args</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The new Python APIs for receiving and raising audit hooks are:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Add an auditing hook</span>
|
||
<span class="n">sys</span><span class="o">.</span><span class="n">addaudithook</span><span class="p">(</span><span class="n">hook</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">]])</span>
|
||
|
||
<span class="c1"># Raise an event with all auditing hooks</span>
|
||
<span class="n">sys</span><span class="o">.</span><span class="n">audit</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Hooks are added by calling <code class="docutils literal notranslate"><span class="pre">PySys_AddAuditHook()</span></code> from C at any time,
|
||
including before <code class="docutils literal notranslate"><span class="pre">Py_Initialize()</span></code>, or by calling
|
||
<code class="docutils literal notranslate"><span class="pre">sys.addaudithook()</span></code> from Python code. Hooks cannot be removed or
|
||
replaced. For CPython, hooks added from C are global, while hooks added
|
||
from Python are only for the current interpreter. Global hooks are
|
||
executed before interpreter hooks.</p>
|
||
<p>When events of interest are occurring, code can either call
|
||
<code class="docutils literal notranslate"><span class="pre">PySys_Audit()</span></code> from C (while the GIL is held) or <code class="docutils literal notranslate"><span class="pre">sys.audit()</span></code>. The
|
||
string argument is the name of the event, and the tuple contains
|
||
arguments. A given event name should have a fixed schema for arguments,
|
||
which should be considered a public API (for each x.y version release),
|
||
and thus should only change between feature releases with updated
|
||
documentation. To minimize overhead and simplify handling in native code
|
||
hook implementations, named arguments are not supported.</p>
|
||
<p>For maximum compatibility, events using the same name as an event in
|
||
the reference interpreter CPython should make every attempt to use
|
||
compatible arguments. Including the name or an abbreviation of the
|
||
implementation in implementation-specific event names will also help
|
||
prevent collisions. For example, a <code class="docutils literal notranslate"><span class="pre">pypy.jit_invoked</span></code> event is clearly
|
||
distinguished from an <code class="docutils literal notranslate"><span class="pre">ipy.jit_invoked</span></code> event. Events raised from
|
||
Python modules should include their module or package name in the event
|
||
name.</p>
|
||
<p>While event names may be arbitrary UTF-8 strings, for consistency across
|
||
implementations it is recommended to use valid Python dotted names and
|
||
avoid encoding specific details in the name. For example, an <code class="docutils literal notranslate"><span class="pre">import</span></code>
|
||
event with the module name <code class="docutils literal notranslate"><span class="pre">spam</span></code> as an argument is preferable to a
|
||
<code class="docutils literal notranslate"><span class="pre">spam</span> <span class="pre">module</span> <span class="pre">imported</span></code> event with no arguments. Avoid using embedded
|
||
null characters or you may upset those who implement hooks using C.</p>
|
||
<p>When an event is audited, each hook is called in the order it was added
|
||
(as much as is possible), passing the event name and arguments. If any
|
||
hook returns with an exception set, later hooks are ignored and <em>in
|
||
general</em> the Python runtime should terminate - exceptions from hooks are
|
||
not intended to be handled or treated as expected occurrences. This
|
||
allows hook implementations to decide how to respond to any particular
|
||
event. The typical responses will be to log the event, abort the
|
||
operation with an exception, or to immediately terminate the process with
|
||
an operating system exit call.</p>
|
||
<p>When an event is audited but no hooks have been set, the <code class="docutils literal notranslate"><span class="pre">audit()</span></code>
|
||
function should impose minimal overhead. Ideally, each argument is a
|
||
reference to existing data rather than a value calculated just for the
|
||
auditing call.</p>
|
||
<p>As hooks may be Python objects, they need to be freed during
|
||
interpreter or runtime finalization. These should not be triggered at
|
||
any other time, and should raise an event hook to ensure that any
|
||
unexpected calls are observed.</p>
|
||
<p>Below in <a class="reference internal" href="#suggested-audit-hook-locations">Suggested Audit Hook Locations</a>, we recommend some important
|
||
operations that should raise audit events. In general, events should be
|
||
raised at the lowest possible level. Given the choice between raising an
|
||
event from Python code or native code, raising from native code should be
|
||
preferred.</p>
|
||
<p>Python implementations should document which operations will raise
|
||
audit events, along with the event schema. It is intentional that
|
||
<code class="docutils literal notranslate"><span class="pre">sys.addaudithook(print)</span></code> is a trivial way to display all messages.</p>
|
||
</section>
|
||
<section id="verified-open-hook">
|
||
<h3><a class="toc-backref" href="#verified-open-hook" role="doc-backlink">Verified Open Hook</a></h3>
|
||
<p>Most operating systems have a mechanism to distinguish between files
|
||
that can be executed and those that can not. For example, this may be an
|
||
execute bit in the permissions field, a verified hash of the file
|
||
contents to detect potential code tampering, or file system path
|
||
restrictions. These are an important security mechanism for ensuring
|
||
that only code that has been approved for a given environment is
|
||
executed.</p>
|
||
<p>Most kernels offer ways to restrict or audit binaries loaded and executed
|
||
by the kernel. File types owned by Python appear as regular data and
|
||
these features do not apply. This open hook allows Python embedders to
|
||
integrate with operating system support when launching scripts or
|
||
importing Python code.</p>
|
||
<p>The new public C API for the verified open hook is:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Set the handler</span>
|
||
<span class="n">typedef</span> <span class="n">PyObject</span> <span class="o">*</span><span class="p">(</span><span class="o">*</span><span class="n">hook_func</span><span class="p">)(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">path</span><span class="p">,</span> <span class="n">void</span> <span class="o">*</span><span class="n">userData</span><span class="p">)</span>
|
||
<span class="nb">int</span> <span class="n">PyFile_SetOpenCodeHook</span><span class="p">(</span><span class="n">hook_func</span> <span class="n">handler</span><span class="p">,</span> <span class="n">void</span> <span class="o">*</span><span class="n">userData</span><span class="p">)</span>
|
||
|
||
<span class="c1"># Open a file using the handler</span>
|
||
<span class="n">PyObject</span> <span class="o">*</span><span class="n">PyFile_OpenCode</span><span class="p">(</span><span class="n">const</span> <span class="n">char</span> <span class="o">*</span><span class="n">path</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The new public Python API for the verified open hook is:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Open a file using the handler</span>
|
||
<span class="n">io</span><span class="o">.</span><span class="n">open_code</span><span class="p">(</span><span class="n">path</span> <span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">io</span><span class="o">.</span><span class="n">IOBase</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">io.open_code()</span></code> function is a drop-in replacement for
|
||
<code class="docutils literal notranslate"><span class="pre">open(abspath(str(pathlike)),</span> <span class="pre">'rb')</span></code>. Its default behaviour is to
|
||
open a file for raw, binary access. To change the behaviour a new
|
||
handler should be set. Handler functions only accept <code class="docutils literal notranslate"><span class="pre">str</span></code> arguments.
|
||
The C API <code class="docutils literal notranslate"><span class="pre">PyFile_OpenCode</span></code> function assumes UTF-8 encoding. Paths
|
||
must be absolute, and it is the responsibility of the caller to ensure
|
||
the full path is correctly resolved.</p>
|
||
<p>A custom handler may be set by calling <code class="docutils literal notranslate"><span class="pre">PyFile_SetOpenCodeHook()</span></code> from
|
||
C at any time, including before <code class="docutils literal notranslate"><span class="pre">Py_Initialize()</span></code>. However, if a hook
|
||
has already been set then the call will fail. When <code class="docutils literal notranslate"><span class="pre">open_code()</span></code> is
|
||
called with a hook set, the hook will be passed the path and its return
|
||
value will be returned directly. The returned object should be an open
|
||
file-like object that supports reading raw bytes. This is explicitly
|
||
intended to allow a <code class="docutils literal notranslate"><span class="pre">BytesIO</span></code> instance if the open handler has already
|
||
read the entire file into memory.</p>
|
||
<p>Note that these hooks can import and call the <code class="docutils literal notranslate"><span class="pre">_io.open()</span></code> function on
|
||
CPython without triggering themselves. They can also use <code class="docutils literal notranslate"><span class="pre">_io.BytesIO</span></code>
|
||
to return a compatible result using an in-memory buffer.</p>
|
||
<p>If the hook determines that the file should not be loaded, it should
|
||
raise an exception of its choice, as well as performing any other
|
||
logging.</p>
|
||
<p>All import and execution functionality involving code from a file will
|
||
be changed to use <code class="docutils literal notranslate"><span class="pre">open_code()</span></code> unconditionally. It is important to
|
||
note that calls to <code class="docutils literal notranslate"><span class="pre">compile()</span></code>, <code class="docutils literal notranslate"><span class="pre">exec()</span></code> and <code class="docutils literal notranslate"><span class="pre">eval()</span></code> do not go
|
||
through this function - an audit hook that includes the code from these
|
||
calls is the best opportunity to validate code that is read from the
|
||
file. Given the current decoupling between import and execution in
|
||
Python, most imported code will go through both <code class="docutils literal notranslate"><span class="pre">open_code()</span></code> and the
|
||
log hook for <code class="docutils literal notranslate"><span class="pre">compile</span></code>, and so care should be taken to avoid
|
||
repeating verification steps.</p>
|
||
<p>File accesses that are not intentionally planning to execute code are
|
||
not expected to use this function. This includes loading pickles, XML
|
||
or YAML files, where code execution is generally considered malicious
|
||
rather than intentional. These operations should provide their own
|
||
auditing events, preferably distinguishing between normal functionality
|
||
(for example, <code class="docutils literal notranslate"><span class="pre">Unpickler.load</span></code>) and code execution
|
||
(<code class="docutils literal notranslate"><span class="pre">Unpickler.find_class</span></code>).</p>
|
||
<p>A few examples: if the file type normally requires an execute bit (on
|
||
POSIX) or would warn when marked as having been downloaded from the
|
||
internet (on Windows), it should probably use <code class="docutils literal notranslate"><span class="pre">open_code()</span></code> rather
|
||
than plain <code class="docutils literal notranslate"><span class="pre">open()</span></code>. Opening ZIP files using the <code class="docutils literal notranslate"><span class="pre">ZipFile</span></code> class
|
||
should use <code class="docutils literal notranslate"><span class="pre">open()</span></code>, while opening them via <code class="docutils literal notranslate"><span class="pre">zipimport</span></code> should use
|
||
<code class="docutils literal notranslate"><span class="pre">open_code()</span></code> to signal the correct intent. Code that uses the wrong
|
||
function for a particular context may bypass the hook, which in CPython
|
||
and the standard library should be considered a bug. Using a combination
|
||
of <code class="docutils literal notranslate"><span class="pre">open_code</span></code> hooks and auditing hooks is necessary to trace all
|
||
executed sources in the presence of arbitrary code.</p>
|
||
<p>There is no Python API provided for changing the open hook. To modify
|
||
import behavior from Python code, use the existing functionality
|
||
provided by <code class="docutils literal notranslate"><span class="pre">importlib</span></code>.</p>
|
||
</section>
|
||
<section id="api-availability">
|
||
<h3><a class="toc-backref" href="#api-availability" role="doc-backlink">API Availability</a></h3>
|
||
<p>While all the functions added here are considered public and stable API,
|
||
the behavior of the functions is implementation specific. Most
|
||
descriptions here refer to the CPython implementation, and while other
|
||
implementations should provide the functions, there is no requirement
|
||
that they behave the same.</p>
|
||
<p>For example, <code class="docutils literal notranslate"><span class="pre">sys.addaudithook()</span></code> and <code class="docutils literal notranslate"><span class="pre">sys.audit()</span></code> should exist but
|
||
may do nothing. This allows code to make calls to <code class="docutils literal notranslate"><span class="pre">sys.audit()</span></code>
|
||
without having to test for existence, but it should not assume that its
|
||
call will have any effect. (Including existence tests in
|
||
security-critical code allows another vector to bypass auditing, so it
|
||
is preferable that the function always exist.)</p>
|
||
<p><code class="docutils literal notranslate"><span class="pre">io.open_code(path)</span></code> should at a minimum always return
|
||
<code class="docutils literal notranslate"><span class="pre">_io.open(path,</span> <span class="pre">'rb')</span></code>. Code using the function should make no further
|
||
assumptions about what may occur, and implementations other than CPython
|
||
are not required to let developers override the behavior of this
|
||
function with a hook.</p>
|
||
</section>
|
||
</section>
|
||
<section id="suggested-audit-hook-locations">
|
||
<h2><a class="toc-backref" href="#suggested-audit-hook-locations" role="doc-backlink">Suggested Audit Hook Locations</a></h2>
|
||
<p>The locations and parameters in calls to <code class="docutils literal notranslate"><span class="pre">sys.audit()</span></code> or
|
||
<code class="docutils literal notranslate"><span class="pre">PySys_Audit()</span></code> are to be determined by individual Python
|
||
implementations. This is to allow maximum freedom for implementations
|
||
to expose the operations that are most relevant to their platform,
|
||
and to avoid or ignore potentially expensive or noisy events.</p>
|
||
<p>Table 1 acts as both suggestions of operations that should trigger
|
||
audit events on all implementations, and examples of event schemas.</p>
|
||
<p>Table 2 provides further examples that are not required, but are
|
||
likely to be available in CPython.</p>
|
||
<p>Refer to the documentation associated with your version of Python to
|
||
see which operations provide audit events.</p>
|
||
<table class="docutils align-default" id="id5">
|
||
<caption><span class="caption-text">Table 1: Suggested Audit Hooks</span></caption>
|
||
<colgroup>
|
||
<col style="width: 15.4%" />
|
||
<col style="width: 15.4%" />
|
||
<col style="width: 23.1%" />
|
||
<col style="width: 46.2%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr class="row-odd"><th class="head">API Function</th>
|
||
<th class="head">Event Name</th>
|
||
<th class="head">Arguments</th>
|
||
<th class="head">Rationale</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">PySys_AddAuditHook</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">sys.addaudithook</span></code></td>
|
||
<td></td>
|
||
<td>Detect when new
|
||
audit hooks are being added.</td>
|
||
</tr>
|
||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">PyFile_SetOpenCodeHook</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">cpython.PyFile_SetOpenCodeHook</span></code></td>
|
||
<td></td>
|
||
<td>Detects any attempt to set the <code class="docutils literal notranslate"><span class="pre">open_code</span></code> hook.</td>
|
||
</tr>
|
||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">compile</span></code>, <code class="docutils literal notranslate"><span class="pre">exec</span></code>, <code class="docutils literal notranslate"><span class="pre">eval</span></code>, <code class="docutils literal notranslate"><span class="pre">PyAst_CompileString</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">PyAST_obj2mod</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">compile</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(code,</span> <span class="pre">filename_or_none)</span></code></td>
|
||
<td>Detect dynamic code compilation, where <code class="docutils literal notranslate"><span class="pre">code</span></code> could be a string or
|
||
AST. Note that this will be called for regular imports of source
|
||
code, including those that were opened with <code class="docutils literal notranslate"><span class="pre">open_code</span></code>.</td>
|
||
</tr>
|
||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">exec</span></code>, <code class="docutils literal notranslate"><span class="pre">eval</span></code>, <code class="docutils literal notranslate"><span class="pre">run_mod</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">exec</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(code_object,)</span></code></td>
|
||
<td>Detect dynamic execution of code objects. This only occurs for
|
||
explicit calls, and is not raised for normal function invocation.</td>
|
||
</tr>
|
||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">import</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">import</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(module,</span> <span class="pre">filename,</span> <span class="pre">sys.path,</span>
|
||
<span class="pre">sys.meta_path,</span> <span class="pre">sys.path_hooks)</span></code></td>
|
||
<td>Detect when modules are
|
||
imported. This is raised before the module name is resolved to a
|
||
file. All arguments other than the module name may be <code class="docutils literal notranslate"><span class="pre">None</span></code> if
|
||
they are not used or available.</td>
|
||
</tr>
|
||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">open</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">io.open</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(path,</span> <span class="pre">mode,</span> <span class="pre">flags)</span></code></td>
|
||
<td>Detect when a
|
||
file is about to be opened. <em>path</em> and <em>mode</em> are the usual parameters
|
||
to <code class="docutils literal notranslate"><span class="pre">open</span></code> if available, while <em>flags</em> is provided instead of <em>mode</em>
|
||
in some cases.</td>
|
||
</tr>
|
||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">PyEval_SetProfile</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">sys.setprofile</span></code></td>
|
||
<td></td>
|
||
<td>Detect when code is
|
||
injecting trace functions. Because of the implementation, exceptions
|
||
raised from the hook will abort the operation, but will not be
|
||
raised in Python code. Note that <code class="docutils literal notranslate"><span class="pre">threading.setprofile</span></code> eventually
|
||
calls this function, so the event will be audited for each thread.</td>
|
||
</tr>
|
||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">PyEval_SetTrace</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">sys.settrace</span></code></td>
|
||
<td></td>
|
||
<td>Detect when code is
|
||
injecting trace functions. Because of the implementation, exceptions
|
||
raised from the hook will abort the operation, but will not be
|
||
raised in Python code. Note that <code class="docutils literal notranslate"><span class="pre">threading.settrace</span></code> eventually
|
||
calls this function, so the event will be audited for each thread.</td>
|
||
</tr>
|
||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">_PyObject_GenericSetAttr</span></code>, <code class="docutils literal notranslate"><span class="pre">check_set_special_type_attr</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">object_set_class</span></code>, <code class="docutils literal notranslate"><span class="pre">func_set_code</span></code>, <code class="docutils literal notranslate"><span class="pre">func_set_[kw]defaults</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">object.__setattr__</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(object,</span> <span class="pre">attr,</span> <span class="pre">value)</span></code></td>
|
||
<td>Detect monkey
|
||
patching of types and objects. This event
|
||
is raised for the <code class="docutils literal notranslate"><span class="pre">__class__</span></code> attribute and any attribute on
|
||
<code class="docutils literal notranslate"><span class="pre">type</span></code> objects.</td>
|
||
</tr>
|
||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">_PyObject_GenericSetAttr</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">object.__delattr__</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(object,</span>
|
||
<span class="pre">attr)</span></code></td>
|
||
<td>Detect deletion of object attributes. This event is raised
|
||
for any attribute on <code class="docutils literal notranslate"><span class="pre">type</span></code> objects.</td>
|
||
</tr>
|
||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">Unpickler.find_class</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">pickle.find_class</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(module_name,</span>
|
||
<span class="pre">global_name)</span></code></td>
|
||
<td>Detect imports and global name lookup when
|
||
unpickling.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<table class="docutils align-default" id="id6">
|
||
<caption><span class="caption-text">Table 2: Potential CPython Audit Hooks</span></caption>
|
||
<colgroup>
|
||
<col style="width: 15.4%" />
|
||
<col style="width: 15.4%" />
|
||
<col style="width: 23.1%" />
|
||
<col style="width: 46.2%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr class="row-odd"><th class="head">API Function</th>
|
||
<th class="head">Event Name</th>
|
||
<th class="head">Arguments</th>
|
||
<th class="head">Rationale</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">_PySys_ClearAuditHooks</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">sys._clearaudithooks</span></code></td>
|
||
<td></td>
|
||
<td>Notifies
|
||
hooks they are being cleaned up, mainly in case the event is
|
||
triggered unexpectedly. This event cannot be aborted.</td>
|
||
</tr>
|
||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">code_new</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">code.__new__</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(bytecode,</span> <span class="pre">filename,</span> <span class="pre">name)</span></code></td>
|
||
<td>Detect dynamic creation of code objects. This only occurs for
|
||
direct instantiation, and is not raised for normal compilation.</td>
|
||
</tr>
|
||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">func_new_impl</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">function.__new__</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(code,)</span></code></td>
|
||
<td>Detect
|
||
dynamic creation of function objects. This only occurs for direct
|
||
instantiation, and is not raised for normal compilation.</td>
|
||
</tr>
|
||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">_ctypes.dlopen</span></code>, <code class="docutils literal notranslate"><span class="pre">_ctypes.LoadLibrary</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">ctypes.dlopen</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(module_or_path,)</span></code></td>
|
||
<td>Detect when native modules are used.</td>
|
||
</tr>
|
||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">_ctypes._FuncPtr</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">ctypes.dlsym</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(lib_object,</span> <span class="pre">name)</span></code></td>
|
||
<td>Collect information about specific symbols retrieved from native
|
||
modules.</td>
|
||
</tr>
|
||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">_ctypes._CData</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">ctypes.cdata</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(ptr_as_int,)</span></code></td>
|
||
<td>Detect
|
||
when code is accessing arbitrary memory using <code class="docutils literal notranslate"><span class="pre">ctypes</span></code>.</td>
|
||
</tr>
|
||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">new_mmap_object</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">mmap.__new__</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(fileno,</span> <span class="pre">map_size,</span> <span class="pre">access,</span>
|
||
<span class="pre">offset)</span></code></td>
|
||
<td>Detects creation of mmap objects. On POSIX, access may
|
||
have been calculated from the <code class="docutils literal notranslate"><span class="pre">prot</span></code> and <code class="docutils literal notranslate"><span class="pre">flags</span></code> arguments.</td>
|
||
</tr>
|
||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">sys._getframe</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">sys._getframe</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(frame_object,)</span></code></td>
|
||
<td>Detect
|
||
when code is accessing frames directly.</td>
|
||
</tr>
|
||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">sys._current_frames</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">sys._current_frames</span></code></td>
|
||
<td></td>
|
||
<td>Detect when
|
||
code is accessing frames directly.</td>
|
||
</tr>
|
||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">socket.bind</span></code>, <code class="docutils literal notranslate"><span class="pre">socket.connect</span></code>, <code class="docutils literal notranslate"><span class="pre">socket.connect_ex</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">socket.getaddrinfo</span></code>, <code class="docutils literal notranslate"><span class="pre">socket.getnameinfo</span></code>, <code class="docutils literal notranslate"><span class="pre">socket.sendmsg</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">socket.sendto</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">socket.address</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(socket,</span> <span class="pre">address,)</span></code></td>
|
||
<td>Detect access to network resources. The address is unmodified from
|
||
the original call.</td>
|
||
</tr>
|
||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">member_get</span></code>, <code class="docutils literal notranslate"><span class="pre">func_get_code</span></code>, <code class="docutils literal notranslate"><span class="pre">func_get_[kw]defaults</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">object.__getattr__</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(object,</span> <span class="pre">attr)</span></code></td>
|
||
<td>Detect access to
|
||
restricted attributes. This event is raised for any built-in
|
||
members that are marked as restricted, and members that may allow
|
||
bypassing imports.</td>
|
||
</tr>
|
||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">urllib.urlopen</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">urllib.Request</span></code></td>
|
||
<td><code class="docutils literal notranslate"><span class="pre">(url,</span> <span class="pre">data,</span> <span class="pre">headers,</span>
|
||
<span class="pre">method)</span></code></td>
|
||
<td>Detects URL requests.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</section>
|
||
<section id="performance-impact">
|
||
<h2><a class="toc-backref" href="#performance-impact" role="doc-backlink">Performance Impact</a></h2>
|
||
<p>The important performance impact is the case where events are being
|
||
raised but there are no hooks attached. This is the unavoidable case -
|
||
once a developer has added audit hooks they have explicitly chosen to
|
||
trade performance for functionality. Performance impact with hooks added
|
||
are not of interest here, since this is opt-in functionality.</p>
|
||
<p>Analysis using the Python Performance Benchmark Suite <a class="footnote-reference brackets" href="#id3" id="id1">[1]</a> shows no
|
||
significant impact, with the vast majority of benchmarks showing
|
||
between 1.05x faster to 1.05x slower.</p>
|
||
<p>In our opinion, the performance impact of the set of auditing points
|
||
described in this PEP is negligible.</p>
|
||
</section>
|
||
<section id="rejected-ideas">
|
||
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2>
|
||
<section id="separate-module-for-audit-hooks">
|
||
<h3><a class="toc-backref" href="#separate-module-for-audit-hooks" role="doc-backlink">Separate module for audit hooks</a></h3>
|
||
<p>The proposal is to add a new module for audit hooks, hypothetically
|
||
<code class="docutils literal notranslate"><span class="pre">audit</span></code>. This would separate the API and implementation from the
|
||
<code class="docutils literal notranslate"><span class="pre">sys</span></code> module, and allow naming the C functions <code class="docutils literal notranslate"><span class="pre">PyAudit_AddHook</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">PyAudit_Audit</span></code> rather than the current variations.</p>
|
||
<p>Any such module would need to be a built-in module that is guaranteed to
|
||
always be present. The nature of these hooks is that they must be
|
||
callable without condition, as any conditional imports or calls provide
|
||
opportunities to intercept and suppress or modify events.</p>
|
||
<p>Given it is one of the most core modules, the <code class="docutils literal notranslate"><span class="pre">sys</span></code> module is somewhat
|
||
protected against module shadowing attacks. Replacing <code class="docutils literal notranslate"><span class="pre">sys</span></code> with a
|
||
sufficiently functional module that the application can still run is a
|
||
much more complicated task than replacing a module with only one
|
||
function of interest. An attacker that has the ability to shadow the
|
||
<code class="docutils literal notranslate"><span class="pre">sys</span></code> module is already capable of running arbitrary code from files,
|
||
whereas an <code class="docutils literal notranslate"><span class="pre">audit</span></code> module could be replaced with a single line in a
|
||
<code class="docutils literal notranslate"><span class="pre">.pth</span></code> file anywhere on the search path:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">sys</span><span class="p">;</span> <span class="n">sys</span><span class="o">.</span><span class="n">modules</span><span class="p">[</span><span class="s1">'audit'</span><span class="p">]</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="s1">'audit'</span><span class="p">,</span> <span class="p">(</span><span class="nb">object</span><span class="p">,),</span>
|
||
<span class="p">{</span><span class="s1">'audit'</span><span class="p">:</span> <span class="k">lambda</span> <span class="o">*</span><span class="n">a</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="s1">'addhook'</span><span class="p">:</span> <span class="k">lambda</span> <span class="o">*</span><span class="n">a</span><span class="p">:</span> <span class="kc">None</span><span class="p">})</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Multiple layers of protection already exist for monkey patching attacks
|
||
against either <code class="docutils literal notranslate"><span class="pre">sys</span></code> or <code class="docutils literal notranslate"><span class="pre">audit</span></code>, but assignments or insertions to
|
||
<code class="docutils literal notranslate"><span class="pre">sys.modules</span></code> are not audited.</p>
|
||
<p>This idea is rejected because it makes it trivial to suppress all calls
|
||
to <code class="docutils literal notranslate"><span class="pre">audit</span></code>.</p>
|
||
</section>
|
||
<section id="flag-in-sys-flags-to-indicate-audited-mode">
|
||
<h3><a class="toc-backref" href="#flag-in-sys-flags-to-indicate-audited-mode" role="doc-backlink">Flag in sys.flags to indicate “audited” mode</a></h3>
|
||
<p>The proposal is to add a value in <code class="docutils literal notranslate"><span class="pre">sys.flags</span></code> to indicate when Python
|
||
is running in a “secure” or “audited” mode. This would allow
|
||
applications to detect when some features are enabled or when hooks
|
||
have been added and modify their behaviour appropriately.</p>
|
||
<p>Currently, we are not aware of any legitimate reasons for a program to
|
||
behave differently in the presence of audit hooks.</p>
|
||
<p>Both application-level APIs <code class="docutils literal notranslate"><span class="pre">sys.audit</span></code> and <code class="docutils literal notranslate"><span class="pre">io.open_code</span></code> are
|
||
always present and functional, regardless of whether the regular
|
||
<code class="docutils literal notranslate"><span class="pre">python</span></code> entry point or some alternative entry point is used. Callers
|
||
cannot determine whether any hooks have been added (except by performing
|
||
side-channel analysis), nor do they need to. The calls should be fast
|
||
enough that callers do not need to avoid them, and the program is
|
||
responsible for ensuring that any added hooks are fast enough to not
|
||
affect application performance.</p>
|
||
<p>The argument that this is “security by obscurity” is valid, but
|
||
irrelevant. Security by obscurity is only an issue when there are no
|
||
other protective mechanisms; obscurity as the first step in avoiding
|
||
attack is strongly recommended (see <a class="reference external" href="https://danielmiessler.com/p/security-by-obscurity/">this article</a> for
|
||
discussion).</p>
|
||
<p>This idea is rejected because there are no appropriate reasons for an
|
||
application to change its behaviour based on whether these APIs are in
|
||
use.</p>
|
||
</section>
|
||
</section>
|
||
<section id="why-not-a-sandbox">
|
||
<h2><a class="toc-backref" href="#why-not-a-sandbox" role="doc-backlink">Why Not A Sandbox</a></h2>
|
||
<p>Sandboxing CPython has been attempted many times in the past, and each
|
||
past attempt has failed. Fundamentally, the problem is that certain
|
||
functionality has to be restricted when executing the sandboxed code,
|
||
but otherwise needs to be available for normal operation of Python. For
|
||
example, completely removing the ability to compile strings into
|
||
bytecode also breaks the ability to import modules from source code, and
|
||
if it is not completely removed then there are too many ways to get
|
||
access to that functionality indirectly. There is not yet any feasible
|
||
way to generically determine whether a given operation is “safe” or not.
|
||
Further information and references available at <a class="footnote-reference brackets" href="#id4" id="id2">[2]</a>.</p>
|
||
<p>This proposal does not attempt to restrict functionality, but simply
|
||
exposes the fact that the functionality is being used. Particularly for
|
||
intrusion scenarios, detection is significantly more important than
|
||
early prevention (as early prevention will generally drive attackers to
|
||
use an alternate, less-detectable, approach). The availability of audit
|
||
hooks alone does not change the attack surface of Python in any way, but
|
||
they enable defenders to integrate Python into their environment in ways
|
||
that are currently not possible.</p>
|
||
<p>Since audit hooks have the ability to safely prevent an operation
|
||
occurring, this feature does enable the ability to provide some level of
|
||
sandboxing. In most cases, however, the intention is to enable logging
|
||
rather than creating a sandbox.</p>
|
||
</section>
|
||
<section id="relationship-to-pep-551">
|
||
<h2><a class="toc-backref" href="#relationship-to-pep-551" role="doc-backlink">Relationship to PEP 551</a></h2>
|
||
<p>This API was originally presented as part of
|
||
<a class="pep reference internal" href="../pep-0551/" title="PEP 551 – Security transparency in the Python runtime">PEP 551</a> Security
|
||
Transparency in the Python Runtime.</p>
|
||
<p>For simpler review purposes, and due to the broader applicability of
|
||
these APIs beyond security, the API design is now presented separately.</p>
|
||
<p><a class="pep reference internal" href="../pep-0551/" title="PEP 551 – Security transparency in the Python runtime">PEP 551</a> is an informational PEP discussing how to integrate Python into
|
||
a secure or audited environment.</p>
|
||
</section>
|
||
<section id="references">
|
||
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
|
||
<aside class="footnote-list brackets">
|
||
<aside class="footnote brackets" id="id3" role="doc-footnote">
|
||
<dt class="label" id="id3">[<a href="#id1">1</a>]</dt>
|
||
<dd>Python Performance Benchmark Suite <a class="reference external" href="https://github.com/python/pyperformance">https://github.com/python/pyperformance</a></aside>
|
||
<aside class="footnote brackets" id="id4" role="doc-footnote">
|
||
<dt class="label" id="id4">[<a href="#id2">2</a>]</dt>
|
||
<dd>Python Security model - Sandbox <a class="reference external" href="https://python-security.readthedocs.io/security.html#sandbox">https://python-security.readthedocs.io/security.html#sandbox</a></aside>
|
||
</aside>
|
||
</section>
|
||
<section id="copyright">
|
||
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
|
||
<p>Copyright (c) 2019 by Microsoft Corporation. This material may be
|
||
distributed only subject to the terms and conditions set forth in the
|
||
Open Publication License, v1.0 or later (the latest version is presently
|
||
available at <a class="reference external" href="https://spdx.org/licenses/OPUBL-1.0.html">https://spdx.org/licenses/OPUBL-1.0.html</a>).</p>
|
||
</section>
|
||
</section>
|
||
<hr class="docutils" />
|
||
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0578.rst">https://github.com/python/peps/blob/main/peps/pep-0578.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0578.rst">2024-06-03 14:51:21 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="#background">Background</a></li>
|
||
<li><a class="reference internal" href="#overview-of-changes">Overview of Changes</a><ul>
|
||
<li><a class="reference internal" href="#audit-hook">Audit Hook</a></li>
|
||
<li><a class="reference internal" href="#verified-open-hook">Verified Open Hook</a></li>
|
||
<li><a class="reference internal" href="#api-availability">API Availability</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#suggested-audit-hook-locations">Suggested Audit Hook Locations</a></li>
|
||
<li><a class="reference internal" href="#performance-impact">Performance Impact</a></li>
|
||
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
|
||
<li><a class="reference internal" href="#separate-module-for-audit-hooks">Separate module for audit hooks</a></li>
|
||
<li><a class="reference internal" href="#flag-in-sys-flags-to-indicate-audited-mode">Flag in sys.flags to indicate “audited” mode</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#why-not-a-sandbox">Why Not A Sandbox</a></li>
|
||
<li><a class="reference internal" href="#relationship-to-pep-551">Relationship to PEP 551</a></li>
|
||
<li><a class="reference internal" href="#references">References</a></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-0578.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> |