Skip to content

Instantly share code, notes, and snippets.

@rasky
Last active March 31, 2016 19:40
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rasky/bd91cf01f72bcc931000 to your computer and use it in GitHub Desktop.
Save rasky/bd91cf01f72bcc931000 to your computer and use it in GitHub Desktop.
Implement package signing in PyPI, pip, distutils
PEP: XXXX
Title: Implement package signing in PyPI, pip, distutils
Version: $Revision$
Last-Modified: $Date$
Author: Giovanni Bajo <rasky@develer.com>
Status: Draft
Type: Process
Content-Type: text/x-rst
Created: 25-Jul-2014
Post-History: XX-XXX-XXXX
Abstract
========
This PEP contains a proposal on how to modify PyPI, pip and distutils
to implement end-to-end package signing and verification, with the
goal of increasing the security in distribution and installation of
Python packages.
The proposal outlined in this document is described with concrete
actionables, an implementation timeline, and can be fully
implemented as an alpha in 2-3 months by the author.
[....]
Rationale
=========
While pip and PyPI have been recently updated to switch to a secure
connection at the transport level [xxx ref], there is still no way
for our package toolchain to make sure that a package has not been
tampered, from the moment developers release it to the moment
users install it.
Linux distros have been implementing package signing for a long time,
and their implementation is a good reference in that it shows that
there is a way for the whole chain of security to be completely
transparent for the end-user (who can just install a package with
a simple command like "tool install pkg"), and reasonably easy for
developers to use when releasing packages.
[....]
Goals
=====
This is a list of goals that have been taken into accounts while
writing this proposal. Most of it basically reiterates the current
structure and workflow, stating that the additional security layer
should not impact it unless absolutely necessary.
* Installing a package must be possible through "pip install <name>"
with no additional steps. Specifically, knowing the name
(and no other data) must be sufficient.
* Installing a package must not force the user to take any
security-related decision, eg: through any click-through question
(unless the user explicitly decides that he/she wants to manually
handle trust, eg: a company scenario with ops whitelisting packages).
* pip must be able to verify that the package wasn’t corrupted or tampered,
since originally published by the maintainer, before executing any
code contained in the package (or even better, before even unpacking it
from any container format it might be shipped in).
* pip must be able to verify that the version of the installed package
matches that requested by the user; eg: if the user requested the "latest"
version, the version should be the latest not-hidden version on PyPI.
* pip must be able to securely install packages from internal (eg: company)
file servers, without having to install live applications (eg: a
PyPI instance) on them.
* A maintainer must be able to register on PyPI through a simple login form.
* A maintainer must be able to create a private signing key with a very
simple process, possibly invoking a single command, that should fit
a single short web page.
* A maintainer must be able to reuse the same signing key for different
packages.
* A maintainer must be able to use "setup.py register" and "setup.py upload"
to register and upload a new package, like it is possible now, with no
additional steps required.
* A maintainer must be able to hide and/or remove specific versions of their
package on PyPI, at least through the web interface (like currently
possible).
* PyPI must retain its current user roles (admin, owner, maintainer), with
the current access levels.
* Cryptographic primitives should not be implemented from scratch but should
rely on existing libraries or third-party tools shipped with pip.
General overview of the workflow
================================
We propose the following final workflow (that can be achieved through the
necessary transition periods, software fixes and documentations):
* Maintainers should create a GPG key and upload it to a keyserver. PyPI
might act as a GPG keyserver, or we could select one official third-party
keyserver (there are many available, like subkeys.gpg.net). Notice that GPG
doesn't require trust on the keyserver.
* Maintainers should insert the GPG fingerprint of their key on their PyPI
account, so to notify PyPI of which is the key that will be used to sign
their packages.
* Maintainers should always use GPG signing (through "setup.py upload --sign",
which should eventually become the default) while uploading a package to
PyPI.
* pip should securely download, cache and update a "trust file" from PyPI.
The trust file is a simple text file listing all packages together with the
respective maintainers' GPG fingerprints.
* When downloading a package, pip should download also the associated GPG
signature, check if it is valid, and check if it was made by (one of the)
key listed in the trust file for the specific package.
With this design, PyPI acts as the central trust that all users will use by
default; this means that, by default, our users will trust PyPI when
installing packages. By having a central trust, we punt on the default
chain-of-trust structure offered by GPG as it is impractical for usage
with pip.
Trust file format
=================
The trust file is a simple, public text file that is generated by PyPI and
contains the association between package names and the GPG fingerprints,
also stating the time at which the association was done.
The format proposed is CSV-based, as follows:
# PYPI-TRUST-FILE-V1
1394507183,gevent,8f0bcdb916fb6218
1394572819,greenlet,2a7d732bc19c8059
1406307256,django,4d3b95ed7d749511|5246c681f318ad50|6bdaa2cb67c8ad44
1408248577,gevent,8f0bcdb916fb6218|ca72ab4092049941
The first line is a fixed header, used to handle future versioning. Each line
contains a trust statement on a specific package:
* The first column is the UNIX timestamp at which the trust statement
begins to be valid; we prefer using timestamps over ISO8601 because
its parsing is far more easier for computers and we don't want to put
ISO8601 parsers into the code chain that must be audited for security.
* The second column is the package name.
* The third column is a pipe-separated list of GPG fingerprints which
are declared to be valid for the specified package.
Trust statements are always specified in chronological order, to simplify
client-side parsing; moreover, this makes the file append-only, which brings
some interesting security benefits (outlined in the thread model section).
The trust file might contains multiple trust statement for a single package,
to cover for the evolution of maintainership of a package (or key changes
made by package authors). For instance, in the above example, a gevent package
signature made by the GPG key `ca72` would be considered valid only if
signed after timestamp `1408248577`.
Modifications to pip
====================
This section describes the proposed modifications to pip, to implement
package signature verification.
Downloading and caching of the trust file
-----------------------------------------
The file should be exposed by PyPI on a fixed URL. pip should download this
file and cache it locally for performance (and possibly security).
pip should also save the timestamp at which the file was requested to PyPI
(conservatively, the timestamp at which the request to the remote PyPI server
was initiated). If a package signature's is newer than the trust file's
timestamp, pip should download a new version of the trust file, and bail out
if it can't do that.
Since the trust file is designed to be append-only, HTTP partial GET requests
and/or the HTTP caching header "If-Modified-Since" can be used to efficiently
update the file. Notice that deciding whether to only use partial GETs (thus
enforcing append-only modifications) or not, has some security implications,
discussed in the thread model section.
It is important to notice that advanced users might want to manually edit and
use a copy of such file. For instance, advanced users might want
to strip it down to remove unneeded / unwanted packages (as this reduces the
surface of attack, making it hard to trick the installation process to install
an unwanted compromised packaged as a fake dependency or through a typo), or
to manually verify and approve only specific keys.
To support this scenario, pip should allow an user to use his/her own trust
file, without ever downloading/updating it from PyPI. In this case, the user
does not need to trust PyPI anymore, as they gain control over full end-to-end
authentication of packages. This is particularly well-suited for corporate
environments (ops might want to whitelist packages, either by filtering the
trust file or signing themselves all the packages and using a totally
different trust file) or automatic deployments (it is easy to deploy a trust
file to a remote server before starting to install all the required packages).
When using a manually-edited file, pip should never attempt to update it, nor
overwrite it with the official PyPI version; the local file must be treated as
fully alternative to the PyPI version, and no merging should be attempted
between the two.
TBD:
* GPG key revocations; what happens if a maintainer revokes their GPG key but
forgets to also tell PyPI that the fingerpring is no longer valid? Does it
make sense for PyPI to run a background job to automatically discover
revocations of maintainers' GPG keys?
GPG implementation
------------------
pip needs to perform the following GPG-related activities:
* Downloading of GPG keys from one/multiple keyservers.
* Storage and caching of such keys on the disk ("keystore").
* Parsing of basic GPG key information.
* Checking for GPG key revocation.
* Verifying a GPG signature on a file.
There are two different possibilities:
* Using a pure-Python GPG implementation, such as [XXXX]
* Invoking the external GPG command that must be installed on the computer,
and thus providing good error messages and documentation on where/how to
install it.
While the choice of the best solution is better left to the pip maintainers,
our suggestion would be to use an external GPG command, as a standard GPG
installation has surely received more real-world testing and auditing than
a rarely used Python reimplementation.
When considering using an external GPG implementation, it is important to
notice that:
* [XXXX] Installation of GPG is trivial even on Windows, Mac OSX, plus all Unix
* [XXXX] Users must have GPG only installed, they don't ever need to perform
any manual action with it. A default, silent, unattended installation is
enough. Specifically, they don't need to have a private GPG key themselves.
Checking package signatures
---------------------------
While installing a package, pip should follow the following steps:
* Retrieve also the package signature, served by PyPI.
* If the package signature is newer than the trust-file cached version,
download a new version of the trust file from PyPI (see above).
* Check if the signature is valid. If it's not valid, abort with an error.
* Extract the signature timestamp; check in the trust file which GPG keys
were valid at the moment the signature was made; if the signature was
not made by one of those keys, abort with an error.
* Proceed to install the package.
When validating the signature, it is important to only trust GPG keys listed
in the trust file, irrespective of any trust level configured in the GPG
keychain; assuming a standard GPG installation, this can be done by using the
`--trusted-key LONGID` command line option while verifying the signature.
Notice that, even if we decide to use a pip-specific GPG keychain, we cannot
still leverage the per-key trust support offered by GPG, as in our case we
have more strict rules: in fact, a key is not globaly trusted or not trusted;
it might be trusted or not trusted for a specific package.
In some cases, the GPG keychain might not contain a copy of the required GPG
public keys; pip will then need to tell GPG to go download the keys from an
external keyserver. pip can ship with a list of valid keyservers, but we
should probably agree on one primary keyserver and suggest users to always
upload their keys (at least) there.
[XXXX] TBD:
* re: Command line options to override/skip signatures. In my opinion, we
should surely offer an option to install unsigned packages (see also the
proposed timeline for how to handle the default value for such an option),
but we might consider asking the pip maintainers NOT to add an option to
force installation of a package whose signature is broken or whose key is
not present in the trust file; both users and ops have still many ways to
workaround and force an installation.
* GPG key revokation. We must decide when and if to check for key
revokation. Assuming that we have an automatic job on the PyPI side that
takes immediately offline all packages whose signature was made by
revoked keys, I think we could be lenient on the pip side, and just
check for revokations every once in a while. This would basically cover
cached files / private indexes.
Modifications to PyPI
=====================
This section describes the proposed modifications to pip, to implement
package signing verification.
Allowing maintainers to specify GPG key fingerprints
----------------------------------------------------
PyPI currently allow a maintainer to specify his/her GPG fingerprint using
the short ID format (32-bit hexadecimal value). Alas, short IDs can be
subject to collision attacks; it is in fact possible, even within consumer
computing power, to create a completely different key with the same short ID
[XXX ref].
It is important that we change PyPI to only use the full fingerprint format
(varying size depending on how the key is created, usually 160 bits).
Since PyPI already allows to associate multiple accounts (maintainers) to
a single package, the basic feature of allowing to trust multiple keys for
a package would be obtained without further modifications on the PyPI side.
It might be convenient for an user to register multiple GPG keys for their
account, but that's not mandatory.
Verification of GPG signatures on upload
----------------------------------------
While it is always the end-user's pip instance that finally check the
signature, it makes sense for PyPI to verify GPG signatures of packages being
uploaded, both to avoid maintainer mistakes and possibly prevent some attacks.
Verifying the signature must be done with the following steps:
* Extract from the signature file the GPG fingerprint of the key that
generated it.
* Check if the fingerprint is associated with a owner/maintainer of the
package, bail otherwise.
* Fetch the fingerprint from a keyserver
* If the key is marked as expired or revoked, bail
* Invoke GPG to verify that the signature is correct
* Check that the timestamp of the signature is within a reasonable timeframe,
bail otherwise; currently setup.py upload only supports signing on-the-fly
while uploading, so the signature should probably be within 10 minutes of
the current PyPI server time. (note: make sure PyPI server has a working
NTP server; make sure to bail with a descriptive error, so that the user
can realize if his/her computer has wrong time)
* Check that the package name and version in the metadata matches the package
name and version that was request. Eg: if pip requested django version
1.1.2, it is important to check that the package metadata says that it is
indeed django version 1.1.2.
Generation of trust file
------------------------
As described above, PyPI should be modified to generate and serve the trust
file. This should be straightforward, given the information in the PyPI
database, [....]
Improve general account security on PyPI
----------------------------------------
Given that fingerprints are security-related information on an
user's account on PyPI, we suggest PyPI maintainers to improve the security
of the accounts on PyPI, implementing some or all of the following suggestions:
* Enforce good password entropy on accounts (eg: through zxcvbn [XXX])
* Add optional two-factor authentication through SMS/OTP, even if used only
to access security-related information in the account. As a worse
alternative, authorization emails can be used (see below). For high-profile
packages (eg: download counts above a certain threshold) we might want
to enforce two-factor authentication checks even for package releases.
* Before accessing security-related information, let the user re-enter
their password if the session is stale (so-called "sudo mode").
Moreover, security changes for important packages (eg: packages with
high download counts) might be forcibly delayed by 72 hours, and notified in
a public mailing-list. While this might represent a nuisance for maintainers,
it can be a life saver in some situations.
Improve notifications on PyPI
-----------------------------
To allow for quick mitigation of an ongoing attack, it is important to
change PyPI to be more verbose about what happens, by sending automatic
notification emails to maintainers when important changes are requested.
We propose to send either an email notification (no confirmation required)
or an approval email (click-to-approve) for any security-related change;
this includes:
* Modifying the GPG fingerprint in a package owner or maintainer profile
* Adding a new owner or maintainer to a package
* Changes to the email address of the account
* Changes to the password of the account
* Changes to the two-factor authentication configuration (if any)
* Release of a new version of a package
* Removal (disabling) of the latest version of a package
Such emails should always be sent to all maintainers of the package.
The implementation should make sure that email address changes are handled
correctly (by notifying both the old and the new address).
Moreover, we propose to let any PyPI user register a secondary security email,
where the notifications are sent, in addition to the primary email. This
might be useful to notify a public mailing-list for high-profile projects,
and/or mitigate email compromisation risks.
Modifications to distutils
==========================
This section describes the proposed modifications to distutils, to implement
package signing.
Allow for password-less upload of signed packages
-------------------------------------------------
Currently, distutils creates and uses a file in the user's home directory
called ".pypirc", that contains the PyPI credentials in plaintext. This
violates the industry best practices of never storing credentials in clear
on the filesystem.
With GPG signing, there is no need for using other credentials during uploads.
If I maintainer uploads a package together with its GPG signature, the
signature already authenticates the maintainer’s identity. There is no reason
to require to also specify the PyPI username or password while uploading: PyPI
could simply verify the GPG signature and, if it is correct, allow the upload
without further verifying the identity.
From a security standpoint, it wouldn’t hurt to still specify the username
and password, even if redundant, in addition to the GPG signature, as a form
of second-factor authentication. But:
* As discussed, “setup.py upload” allows to save the maintainer’s password as
cleartext in a file on the filesystem (~/.pypirc). This is very insecure,
but Python does not currently ship with a library to save passwords in
secure way (eg: integrating with OS’ system keychains), and the code would
be quite complicated to write. By removing the need to supply PyPI
credential, we can skip the problem of saving such a password
* All GPG keys are stored in the keychain with a passphrase. This means that
the maintainer will always be required to insert the GPG key password while
signing and uploading. Asking the maintainer to insert a second (PyPI’s)
password seems superflous.
Removing the need for a password-authentication for signed package uploads
seems like the best compromise.
TBD:
* the current protocol for file upload to PyPI specifies that some metadata
about the package be passed as form data and thus would not be signed by
GPG. We need to analyze this requirement together with the removal of user
authentication, to understand if there are some possible attacks made
through forging of such metadata.
* Make sure that it’s not easy to DOS PyPI uploading too many packages with
invalid signatures. Try to mount such an attack and see what happens.
Make "setup.py upload" sign by default
--------------------------------------
As discussed later in the timeline, we propose to make `--sign` the default,
as at some later point we will want to only allow GPG-signed packages to be
uploaded. This change is trivial in the codebase and can be synchronized
with a minor Python release.
Implementation timeline
=======================
This is the prospected schedule and policy for implementation of package
signing:
201X-XX: PyPI implements the required changes and starts serving the trust file.
201X-XX: Communication begins to tell package maintainers to register their
GPG fingerprints in PyPI, and uploads their GPG keys to one or more
of the approved keyservers. We will explain that the switch to GPG
will become mandatory, and as soon as a fingerprint is registered
in PyPI, package signing becomes mandatory for further releases.
201X-XX: pip implements package signature verification, with the following
policy: if a package is mentioned at least once in the trust file,
verification is performed by default and it is mandatory to install
any version of the package newer than the earliest mention in the
trust file; if a package is not mentioned in the trust file, or
the user requests a version that predates the earliest mention of
the package in the trust file, a warning is printed but the
installation continues. Package verification can be still manually
disabled with a command-line option, as a workaround for bugs.
Also, a new command line option is added, that restricts installation
only to signed packages.
201X-XX: A new version of Python is released, which contains an updated
distutils which makes the "--sign" option to distutils the default,
and a "--no-sign" is added to disable it, with a warning.
201X-XX: Communication that on "Day YY", PyPI will stop accept new packages
(or new versions of existing packages) that are not GPG-signed.
201X-XX: pip is updated so to reject any unsigned package released after
Day YY.
201X-YY: Day YY: PyPI is updated to reject new unsigned releases of packages.
This policy should be transparent for users (who update pip [XXX]) and
should not cause any regression. We're conservatively proposing to basically
keep allowing pip install old unsigned packages forever, which means that
we should not break anything.
Threat model
============
This section outlines the attacks that were taked into account in the
design of this PEP.
LAN-level MITM attack
---------------------
In this attack, a MITM attacker sits in the same network of the user installing
a package, and can intercept and alter the network traffic.
Part of this attack has been already partially mitigated by the recent switch
of pip and PyPI to SSL (with proper certificate checking). Alas, this is not
sufficient; in fact:
* pip does not pin to a specific certificate or public key for SSL, so an
attacker might still be able to intercept the traffic by serving a
different domain-valid certificate, generated by a exploiting a bug in a
CA, using a private CA already trusted by the user (eg: an enterprise-level
CA) or by tricking the user into installing a custom CA.
* pip still allows non-SSL installation of packages from third-party websites
or external indexes.
This PEP offers an improvement in this scenario; in fact, thanks to the
end-of-end security of a GPG signature, it is impossible for an attacker to
generate a signature that would be trusted by pip.
On the other hand, assuming that an attacker can fully intercept and modify
the traffic between PyPI and the user, the attacker might still be able to
mount an attack, by faking a new (non existing) release of a package, and
then altering the trust file served by PyPI to whitelist a new GPG key under
the attacker's control, that can be used to generate a valid siganture for
the new package.
While we believe that it is sufficient to further mitigate this attack by
adding certificate pinning to pip, we can discuss if we want to fully fix this
by adding a signature to the PyPI trust file. Such signature would need to be
generated automatically by PyPI any time the trust file is updated, and thus
would need to be kept online (either on disk/memory, or within a HSM). Notice
that adding such signature can be done as an incremental improvement to the
current PEP's design.
Shadowing of private packages through PyPI registration
-------------------------------------------------------
In this scenario, the user is downloading packages from a local index (in
addition to PyPI), in which some private packages are distributed; those
packages are not registered on PyPI. The attacker acquires knowledge of the
name of one of such private packages, register them on PyPI, and publish
a compromised package.
At the moment, pip merges package releases coming from the multiple indexes it
accesses. For instance, if it is configured to access a local index that
serves a package called "creditcard" whose latest version is 1.1, but a
package with the same name appears on PyPI and advertises a newer version, pip
will silently upgrade to the PyPI version if requested.
This PEP offers a solution to this attack; in fact, the local index can serve
a local trust file, which specifies which GPG keys are allowed to sign the
"creditcard" package; thus, pip can detect that the package on PyPI has
a different trust statement and refuse to do an upgrade.
Our suggestion would be to completely disable the automatic merge of release
information of multiple indexes when this PEP is implemented, at least by
default, as this is prone to security mistakes. In case the pip maintainers do
not agree with this, it is sufficient to disable the merging in case of
different trust statements. Notice that any attempt of "merging" trust
statement and/or detecting conflicts must absolutely be avoided; pip should
simply avoid merging in case of different trust statement, without further
reasoning on them.
Registering slight variations of packages names (typos) on PyPI
---------------------------------------------------------------
In this scenario, the attacker registers slight variations of common package
names on PyPI, as an attempt to trick users into downloading a compromised
version of the package. For instance, an attacker might register package
"djang" on PyPI, and then wait for someone to mistype the package name to gain
control of their system.
This PEP does not offer a full solution to this problem; the core issue is
that PyPI is a "package wiki" and not a curated package list; anybody can register
packages on PyPI, and package signing can only verify that the package has been
released by the maintainer registered on PyPI, but not that the package is in
fact the one that the user is expecting to install.
We suggest a partial mitigation for this attack: PyPI could take note of invalid
package accesses; if a specific non-existing package name is accessed more than
a specified threshold (eg: 10 times a month), the package name is marked as
"reserved" on PyPI. Any attempt to register that package name will then be held
for moderation.
Compromisation of a maintainer's PyPI credentials
-------------------------------------------------
In this scenarion, the attacker is in possess of the PyPI user and password of
a package maintainer; we assume that the attacker has not permanently
compromised PyPI itself (eg: obtained shell access). For instance, the
attacker might:
* Have installed a keylogger on the maintainer's computer
* Have bruteforced the credentials through the PyPI login web form or any
HTTP basic-auth API.
* Have obtained access to the maintainer's filesystem which includes a
~/.pypirc file with the credentials stored in plaintext
* Have obtained a copy of the PyPI user database and then bruteforced the
password hash.
This PEP improves the situation in this scenario, because the username
and password are not enough to upload a new package; in fact, packages will
(eventually) be required to be signed, and thus the attacker would need
to compromise the GPG private key, not the PyPI credentials.
With the PyPI credentials, the attacker might login on PyPI and try to
gain control of the package by, eg., adding a key under her control as
valid key for that package's signatures. But even in this case, this PEP
proposes to add a second-factor authentication and/or email verification
for security-related changes, and even delay up to 72 hours any change
to high-profile packages, to give enough time for a public audit.
Compromisation of a maintainer's GPG key
----------------------------------------
In this scenario, the attacker has obtained access to a maintainer's
GPG private key, used to sign a package.
This is one of the worst attacks because a GPG key is what identifies
a maintainers, and thus stealing it allows to fully impersonate the
maintainer.
This PEP allows for some mitigations in this scenario; in fact:
* If the attacker publishes a new (compromised) version of a package,
an email is sent to both the primary email address (and the secondary,
if configure) of all maintainers associated with the package.
This would allow other maintainers to quickly realize that an attack
is going on.
* If the maintainer has configured two-factor authentication, the attacker
would not be able to release the package, because the release would
trigger a second-factor authorization step that the attacker would not
be able to pass.
Compromisation of the PyPI file server
--------------------------------------
In this scenario, the attacker is able to alter packages files on PyPI
(but with no access to PyPI user/credential database). This might apply
also to file mirrors (eg: CDN).
This PEP mitigates this scenario, because modifying a package would
invalidate the GPG siganture, and thus pip would then refuse to install
it. This also means that the user does not need to trust CDNs, proxies
or intermediate caches.
Compromisation of the whole PyPI server
---------------------------------------
In this scenario, the attacker has full control over PyPI; for instance,
the attacker might have gained "root access" to the server running the
PyPI application instance.
This PEP only offers light mitigation against this scenario. The security
tradeoff that was chosen is to force the user to trust PyPI; we reckon
that the tradeoff of going with a central trust rather than a distributed
trust is the best we can do, as that allows users of pip to safely install
packages with zero precedent knowledge and no security questions (aka
click-throughs). It is the same tradeoff used by SSL (but without the
mistake of having a large number of CAs being able to generate
certificates for all domains, which is the core root of the "CA model
is broken" movement).
This said, there is some mitigation offered:
* The attacker cannot simply publish a new version of a package, nor modify
an existing released version, as they don't have access to the GPG private
key. This blocks the simplest attack vector, but it is of course not
sufficient.
* The trust-file is designed to be append-only. The PEP suggests to use
partial-gets to update it, but we might want to upgrade this to a
requirement to enhance security. In this case, if an attacker modifies an
existing line in the trust-file, existing pip users should be shielded as
they would (by default) not re-download the new compromised file; this would
probably lead to some corruption, which in turn might be notified to the
user with some scary message. Notice that signing the trust file with a
master key doesn't help, as an attacker would root access to PyPI would be
able to sign their compromised trust-file as well.
* Advanced users and/or enterprises can use a curated/manually-maintained
trust-file. If the attacker modifies the trust-file on PyPI, these users
would probably be partially shielded, as the modification would eventually
be reviewed by them before being integrated.
The attack which is most likely to go unnoticed for a long time is thus to add
a new GPG fingerprint to a package by adding a line at the end of the trust-
file, and then release a new version of the compromised package, signed with
that GPG key.
It is important to notice that, with pip, it is common practice to create a
`requirements.txt` with `pip freeze`. This means that most projects are
believed to use version-pinning of all third-party dependencies; thus, if the
attacker releases a new version of a package, this version would not
automatically auto-update to all users of that package, making the attack less
powerful and less overreaching. Obviously, the compromised package would still
be served to all users installing the package for the first time (eg: in a new
virtualenv).
Nonetheless, considering the severity of the scenario, we think that it is a
fair achievement.
References
==========
.. [1] PEP 1, PEP Purpose and Guidelines, Warsaw, Hylton
(http://www.python.org/dev/peps/pep-0001)
.. [2] PEP 9, Sample Plaintext PEP Template, Warsaw
(http://www.python.org/dev/peps/pep-0009)
Copyright
=========
This document has been placed in the public domain.
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
sentence-end-double-space: t
fill-column: 70
coding: utf-8
End:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment