November 23, 2021·6 min read·engineeringperlsecuritycpan

CPAN verification must authenticate the checksums it uses

Three disclosed vulnerabilities separate valid signatures from trustworthy package selection. The fixes bind checksums to author paths and tighten what clients accept.

A valid signature somewhere in a file does not establish that the data a package client consumes was signed. That is the central failure in two vulnerabilities disclosed for CPAN clients on November 23. The advisory by Stig Palmquist demonstrates unsigned checksum data preceding a valid signed section. A client can report that signature verification succeeded while installing a package selected by the unsigned content.

A third vulnerability concerns the identity of the signed package. A checksum file that identifies an archive only by its filename can be validly signed for one author's directory and reused under another author's directory. That attack does not require forging a signature. It exploits information missing from what the signature authenticates. Together the issues show why verifying cryptography and verifying the meaning of a package reference must be parts of the same operation.

I value CPAN as part of what makes Perl useful beyond a single script. That also makes the installation path important. A package client needs to preserve the relationship between the requested distribution, the downloaded bytes, and the authenticated metadata that describes them.

The package index and checksum file answer different questions

Neil Bowers's account for PAUSE administrators explains the distribution process. An author uploads an archive to PAUSE, which places it in the author's directory. Indexing permissions determine whether that upload updates the module index. The index associates a module and version with a path to its distribution archive.

PAUSE generates a CHECKSUMS file for each author directory and signs it with GnuPG. A client resolving a module can download the selected archive and compare its computed checksum with the entry in that file. CPAN.pm performs that comparison by default, but signature verification is optional. These are separate checks. A checksum comparison establishes agreement with the metadata supplied to the client. Authenticating the metadata requires another step.

The path matters because different authors can upload archives with the same filename. The repository's directory structure distinguishes them. A signed record that omits that structure does not contain all the information needed to identify which author's upload the client intends to install. A correct hash of the wrong archive remains a correct hash.

This is why the disclosure cannot be reduced to replacing a weak hash algorithm. The advisory describes a verification bypass and an identity-binding problem. An attacker choosing both a replacement archive and matching unauthenticated metadata does not need to find a collision in a cryptographic hash. The missing guarantee is that the client is using authenticated metadata for the requested path.

A signed filename did not bind the author directory

CVE-2020-16155 covers CPAN::Checksums 2.12 in the advisory. The generated records identify archives by filename without the author path. An attacker with PAUSE access can upload a different archive with the same filename to their own directory and obtain a PAUSE-signed checksum file for it. A malicious mirror can then serve those bytes under a different author's path.

The signature is genuine because PAUSE signed data for the attacker's upload. The error appears when a consumer treats that signature as evidence about another directory. Bowers describes copying the other files as well so the directory-level checksum data looks appropriate except for the substituted archive. The attack joins a legitimate signature to an unintended package identity.

PAUSE now includes a cpan_path field giving the relative author-directory path. Updated CPAN.pm checks that it matches the expected location. A checksum file moved from another directory can therefore fail validation even when its signature is valid. The added field supplies identity information the old record lacked.

That fix requires both production and consumption of the new information. Adding a field to metadata does not protect a client that never checks it. Bowers also reports a CPANPLUS update that validates the path. Its existence is relevant because ecosystem security depends on individual client behaviour, not only on what PAUSE emits.

Signature checking could accept unrelated unsigned data

CVE-2020-16154 concerns App::cpanminus 1.7044, while CVE-2020-16156 concerns CPAN 2.28. The advisory explains that an attacker can prepend checksum data before the cleartext PGP headers. In the affected verification path, omitted sigtext and plaintext arguments bypass a comparison in Module::Signature. Verification then establishes only that valid signed cleartext occurs somewhere in the file.

Meanwhile, the client can consume the preceding unsigned checksum data. The verification result and the installation decision concern different content. The advisory's demonstration prints successful signature and checksum messages before running the substituted archive's configuration script. That example does not establish that the cryptographic signature was broken. It establishes that the client failed to require its installation data to be the signed data.

This distinction matters for interpreting a successful log message. A signature check needs an exact association with the bytes subsequently parsed and used. Searching for a valid signature within a larger document is insufficient when another portion of the document can supply the effective package record. The signed section can remain unchanged throughout the attack.

Updated CPAN.pm refuses an unsigned checksum file when configured to require signatures. Bowers explicitly says that cpanminus 1.7044 has not yet addressed the disclosed issues on November 23. Its --verify option should therefore not be described as a completed mitigation for this bypass. Work on rejecting unsigned content and checking cpan_path remains in progress in the account published today.

Client defaults define different protections

CPAN.pm 2.29 uses cpan.org by default and ignores a previously configured urllist unless pushy_https is disabled. It attempts HTTPS, including through curl or wget where the needed Perl modules are missing. The fallback to HTTP remains possible if those approaches fail. That behaviour improves the default source selection but does not constitute a strict HTTPS-only guarantee.

The same client always consults CHECKSUMS. Setting check_sigs requires a valid signature in addition to the checksum comparison. This is a more specific statement than saying the client verifies packages. It identifies which checks are automatic and which depend on configuration. A deployment policy requiring signatures has to enable that setting as well as use a version with the relevant repair.

The disclosure documents different defaults for the other clients. Cpanminus uses HTTP to cpan.org by default and does not consult CHECKSUMS unless asked through --verify. Cpm uses HTTPS and MetaCPAN's mirror by default but does not support checksum-file verification. CPANPLUS has checksum verification under its documented configuration and no option for signature verification. These clients cannot be treated as interchangeable implementations of the same verification policy.

The recommended CPAN.pm shell configuration for selecting a trusted HTTPS source can be written directly as follows. These are commands inside the CPAN shell, not ordinary shell commands.

o conf urllist https://www.cpan.org
o conf commit

For a repaired CPAN.pm installation requiring signatures, Bowers gives a separate setting.

o conf check_sigs true
o conf commit

Selecting the endpoint and requiring signatures address different parts of the installation path. The HTTPS source protects the connection to the selected mirror. The signature requirement adds validation of PAUSE's checksum metadata. Neither declaration should be used to imply that every client has implemented every fix, or that a trusted connection makes missing identity information irrelevant.

The immediate mitigation has a limited claim

Both primary accounts advise using a trusted HTTPS mirror. Bowers names www.cpan.org and cpan.metacpan.org. This reduces exposure to a malicious mirror or an attacker modifying unprotected network traffic. It is a practical response while client fixes differ, but it should be described as trusting a particular source and connection. It does not make an arbitrary mirror trustworthy merely because that mirror serves HTTPS.

The advisory's chronology also explains the apparently older CVE numbers. It records notification of the Perl security team in July 2020, subsequent notification of module authors, and CVE assignment that month. Coordinated publication occurs on November 23, 2021. The identifiers therefore do not date the public disclosure to 2020.

Bowers also identifies a remaining deployment constraint. Perl does not support HTTPS out of the box, so clients cannot yet assume a working native HTTPS transport everywhere. Depending on external download tools improves availability, but the fallback policy still needs to be understood by the operator configuring installations.

I consider the strongest lesson here a specific verification requirement. The checksum record must describe the requested author path, and the client must consume the same data whose signature it validates. November's repairs begin enforcing those relationships, while the published client-by-client account identifies where they remain incomplete.